435 lines
14 KiB
C
435 lines
14 KiB
C
/** @license 2017 Neil Edelman, distributed under the terms of the
|
|
[MIT License](https://opensource.org/licenses/MIT).
|
|
|
|
@abstract Source <src/list.h>; examples <test/test_list.c>.
|
|
|
|
@subtitle Doubly-linked component
|
|
|
|
![Example of a stochastic skip-list.](../web/list.png)
|
|
|
|
In parlance of <Thareja 2014, Structures>, <tag:<L>list> is a circular
|
|
header, or sentinel, to a doubly-linked list of <tag:<L>listlink>. This is a
|
|
closed structure, such that with with a pointer to any element, it is possible
|
|
to extract the entire list.
|
|
|
|
@param[LIST_NAME]
|
|
`<L>` that satisfies `C` naming conventions when mangled; required. `<PL>` is
|
|
private, whose names are prefixed in a manner to avoid collisions.
|
|
|
|
@param[LIST_EXPECT_TRAIT]
|
|
Do not un-define certain variables for subsequent inclusion in a trait.
|
|
|
|
@param[LIST_COMPARE_NAME, LIST_COMPARE, LIST_IS_EQUAL]
|
|
Compare trait contained in <src/list_coda.h>. An optional mangled name for
|
|
uniqueness and a function implementing either <typedef:<PLC>compare_fn> or
|
|
<typedef:<PLC>bipredicate_fn>.
|
|
|
|
@param[LIST_TO_STRING_NAME, LIST_TO_STRING]
|
|
To string trait contained in <src/to_string.h>. An optional mangled name for
|
|
uniqueness and function implementing <typedef:<PSZ>to_string_fn>.
|
|
|
|
@std C89 */
|
|
|
|
#ifndef LIST_NAME
|
|
#error Name LIST_NAME undefined.
|
|
#endif
|
|
#if defined(LIST_TO_STRING_NAME) || defined(LIST_TO_STRING) /* <!-- str */
|
|
#define LIST_TO_STRING_TRAIT 1
|
|
#else /* str --><!-- !str */
|
|
#define LIST_TO_STRING_TRAIT 0
|
|
#endif /* !str --> */
|
|
#if defined(LIST_COMPARE_NAME) || defined(LIST_COMPARE) \
|
|
|| defined(LIST_IS_EQUAL) /* <!-- comp */
|
|
#define LIST_COMPARE_TRAIT 1
|
|
#else /* comp --><!-- !comp */
|
|
#define LIST_COMPARE_TRAIT 0
|
|
#endif /* !comp --> */
|
|
#define LIST_TRAITS LIST_TO_STRING_TRAIT + LIST_COMPARE_TRAIT
|
|
#if LIST_TRAITS > 1
|
|
#error Only one trait per include is allowed; use LIST_EXPECT_TRAIT.
|
|
#endif
|
|
#if LIST_TRAITS && !defined(LIST_BASE)
|
|
#error Trying to define a trait without defining the base datatype.
|
|
#endif
|
|
#if defined(LIST_TO_STRING_NAME) && !defined(LIST_TO_STRING)
|
|
#error LIST_TO_STRING_NAME requires LIST_TO_STRING.
|
|
#endif
|
|
#if defined(LIST_COMPARE_NAME) \
|
|
&& (!(!defined(LIST_COMPARE) ^ !defined(LIST_IS_EQUAL)))
|
|
#error LIST_COMPARE_NAME requires LIST_COMPARE or LIST_IS_EQUAL not both.
|
|
#endif
|
|
|
|
#ifndef LIST_H /* <!-- idempotent */
|
|
#define LIST_H
|
|
#include <assert.h>
|
|
#if defined(LIST_CAT_) || defined(LIST_CAT) || defined(L_) || defined(PL_)
|
|
#error Unexpected defines.
|
|
#endif
|
|
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
|
#define LIST_CAT_(n, m) n ## _ ## m
|
|
#define LIST_CAT(n, m) LIST_CAT_(n, m)
|
|
#define L_(n) LIST_CAT(LIST_NAME, n)
|
|
#define PL_(n) LIST_CAT(list, L_(n))
|
|
#endif /* idempotent --> */
|
|
|
|
|
|
#if LIST_TRAITS == 0 /* <!-- base code */
|
|
#define LIST_BASE
|
|
|
|
|
|
/** Storage of this structure is the responsibility of the caller, who must
|
|
provide a stable pointer while in a list. Generally, one encloses this in a
|
|
host `struct` or `union`. The contents of this structure should be treated as
|
|
read-only while in the list.
|
|
|
|
![States.](../web/node-states.png) */
|
|
struct L_(listlink);
|
|
struct L_(listlink) { struct L_(listlink) *next, *prev; };
|
|
|
|
/** Serves as head and tail for linked-list of <tag:<L>listlink>. Use
|
|
<fn:<L>list_clear> to initialize the list. Because this list is closed; that
|
|
is, given a valid pointer to an element, one can determine all others, null
|
|
values are not allowed and it is _not_ the same as `{0}`. The contents of this
|
|
structure should be treated as read-only while initialized, with the exception
|
|
of <fn:<L>list_self_correct>.
|
|
|
|
![States.](../web/states.png) */
|
|
struct L_(list);
|
|
struct L_(list) {
|
|
union {
|
|
struct { struct L_(listlink) head, *part_of_tail; } as_head;
|
|
struct { struct L_(listlink) *part_of_head, tail; } as_tail;
|
|
struct { struct L_(listlink) *next, *zero, *prev; } flat;
|
|
} u;
|
|
};
|
|
|
|
/** Operates by side-effects on the node. */
|
|
typedef void (*PL_(action_fn))(struct L_(listlink) *);
|
|
|
|
/** Returns (Non-zero) true or (zero) false when given a node. */
|
|
typedef int (*PL_(predicate_fn))(const struct L_(listlink) *);
|
|
|
|
/** Cats all `from` in front of `after`; `from` will be empty after.
|
|
Careful that `after` is not in `from` because that will just erase the list.
|
|
@order \Theta(1) */
|
|
static void PL_(move)(struct L_(list) *const from,
|
|
struct L_(listlink) *const after) {
|
|
assert(from && from->u.flat.next && !from->u.flat.zero && from->u.flat.prev
|
|
&& after && after->prev);
|
|
from->u.flat.next->prev = after->prev;
|
|
after->prev->next = from->u.as_head.head.next;
|
|
from->u.flat.prev->next = after;
|
|
after->prev = from->u.as_tail.tail.prev;
|
|
from->u.flat.next = &from->u.as_tail.tail;
|
|
from->u.flat.prev = &from->u.as_head.head;
|
|
}
|
|
|
|
/** @return A pointer to the first element of `list`, if it exists.
|
|
@order \Theta(1) @allow */
|
|
static struct L_(listlink) *L_(list_head)(const struct L_(list) *const list) {
|
|
struct L_(listlink) *link;
|
|
assert(list);
|
|
link = list->u.flat.next, assert(link);
|
|
return link->next ? link : 0;
|
|
}
|
|
|
|
/** @return A pointer to the last element of `list`, if it exists.
|
|
@order \Theta(1) @allow */
|
|
static struct L_(listlink) *L_(list_tail)(const struct L_(list) *const list) {
|
|
struct L_(listlink) *link;
|
|
assert(list);
|
|
link = list->u.flat.prev, assert(link);
|
|
return link->prev ? link : 0;
|
|
}
|
|
|
|
/** @return The previous element. When `link` is the first element, returns
|
|
null. @order \Theta(1) @allow */
|
|
static struct L_(listlink) *L_(list_previous)(struct L_(listlink) *link) {
|
|
assert(link && link->prev);
|
|
link = link->prev;
|
|
return link->prev ? link : 0;
|
|
}
|
|
|
|
/** @return The next element. When `link` is the last element, returns null.
|
|
@order \Theta(1) @allow */
|
|
static struct L_(listlink) *L_(list_next)(struct L_(listlink) *link) {
|
|
assert(link && link->next);
|
|
link = link->next;
|
|
return link->next ? link : 0;
|
|
}
|
|
|
|
/** Clear `list`. */
|
|
static void PL_(clear)(struct L_(list) *const list) {
|
|
list->u.flat.next = &list->u.as_tail.tail;
|
|
list->u.flat.zero = 0;
|
|
list->u.flat.prev = &list->u.as_head.head;
|
|
}
|
|
|
|
/** Clears and initializes `list`. @order \Theta(1) @allow */
|
|
static void L_(list_clear)(struct L_(list) *const list)
|
|
{ assert(list); PL_(clear)(list); }
|
|
|
|
/** `add` before `anchor`. @order \Theta(1) */
|
|
static void PL_(add_before)(struct L_(listlink) *const anchor,
|
|
struct L_(listlink) *const add) {
|
|
add->prev = anchor->prev;
|
|
add->next = anchor;
|
|
anchor->prev->next = add;
|
|
anchor->prev = add;
|
|
}
|
|
|
|
/** `add` before `anchor`. @order \Theta(1) @allow */
|
|
static void L_(list_add_before)(struct L_(listlink) *const anchor,
|
|
struct L_(listlink) *const add) {
|
|
assert(anchor && add && anchor != add && anchor->prev);
|
|
PL_(add_before)(anchor, add);
|
|
}
|
|
|
|
/** `add` after `anchor`. @order \Theta(1) */
|
|
static void PL_(add_after)(struct L_(listlink) *const anchor,
|
|
struct L_(listlink) *const add) {
|
|
add->prev = anchor;
|
|
add->next = anchor->next;
|
|
anchor->next->prev = add;
|
|
anchor->next = add;
|
|
}
|
|
|
|
/** `add` after `anchor`. @order \Theta(1) @allow */
|
|
static void L_(list_add_after)(struct L_(listlink) *const anchor,
|
|
struct L_(listlink) *const add) {
|
|
assert(anchor && add && anchor != add && anchor->next);
|
|
PL_(add_after)(anchor, add);
|
|
}
|
|
|
|
/** Adds `add` to the end of `list`. @order \Theta(1) */
|
|
static void PL_(push)(struct L_(list) *const list,
|
|
struct L_(listlink) *const add)
|
|
{ PL_(add_before)(&list->u.as_tail.tail, add); }
|
|
|
|
/** Adds `add` to the end of `list`. @order \Theta(1) @allow */
|
|
static void L_(list_push)(struct L_(list) *const list,
|
|
struct L_(listlink) *const add)
|
|
{ assert(list && add), PL_(push)(list, add); }
|
|
|
|
/** Adds `add` to the beginning of `list`. @order \Theta(1) @allow */
|
|
static void L_(list_unshift)(struct L_(list) *const list,
|
|
struct L_(listlink) *const add)
|
|
{ assert(list && add), PL_(add_after)(&list->u.as_head.head, add); }
|
|
|
|
/** Remove `node`. @order \Theta(1) */
|
|
static void PL_(remove)(struct L_(listlink) *const node) {
|
|
node->prev->next = node->next;
|
|
node->next->prev = node->prev;
|
|
node->prev = node->next = 0;
|
|
}
|
|
|
|
/** Remove `node`. @order \Theta(1) @allow */
|
|
static void L_(list_remove)(struct L_(listlink) *const node)
|
|
{ assert(node && node->prev && node->next), PL_(remove)(node); }
|
|
|
|
/** Removes the first element of `list` and returns it, if any.
|
|
@order \Theta(1) @allow */
|
|
static struct L_(listlink) *L_(list_shift)(struct L_(list) *const list) {
|
|
struct L_(listlink) *node;
|
|
assert(list && list->u.flat.next);
|
|
if(!(node = list->u.flat.next)->next) return 0;
|
|
L_(list_remove)(node);
|
|
return node;
|
|
}
|
|
|
|
/** Removes the last element of `list` and returns it, if any.
|
|
@order \Theta(1) @allow */
|
|
static struct L_(listlink) *L_(list_pop)(struct L_(list) *const list) {
|
|
struct L_(listlink) *node;
|
|
assert(list && list->u.flat.prev);
|
|
if(!(node = list->u.flat.prev)->prev) return 0;
|
|
L_(list_remove)(node);
|
|
return node;
|
|
}
|
|
|
|
/** Moves the elements `from` onto `to` at the end.
|
|
@param[to] If null, then it removes elements from `from`.
|
|
@order \Theta(1) @allow */
|
|
static void L_(list_to)(struct L_(list) *const from,
|
|
struct L_(list) *const to) {
|
|
assert(from && from != to);
|
|
if(!to) { PL_(clear)(from); return; }
|
|
PL_(move)(from, &to->u.as_tail.tail);
|
|
}
|
|
|
|
/** Moves the elements `from` immediately before `anchor`, which can not be in
|
|
the same list. @order \Theta(1) @allow */
|
|
static void L_(list_to_before)(struct L_(list) *const from,
|
|
struct L_(listlink) *const anchor) {
|
|
assert(from && anchor);
|
|
PL_(move)(from, anchor);
|
|
}
|
|
|
|
/** Moves all elements `from` onto `to` at the tail if `predicate` is true.
|
|
They ca'n't be the same list.
|
|
@param[to] If null, then it removes elements.
|
|
@order \Theta(|`from`|) \times \O(`predicate`) @allow */
|
|
static void L_(list_to_if)(struct L_(list) *const from,
|
|
struct L_(list) *const to, const PL_(predicate_fn) predicate) {
|
|
struct L_(listlink) *link, *next_link;
|
|
assert(from && from != to && predicate);
|
|
for(link = from->u.flat.next; next_link = link->next; link = next_link) {
|
|
if(!predicate(link)) continue;
|
|
L_(list_remove)(link);
|
|
if(to) L_(list_add_before)(&to->u.as_tail.tail, link);
|
|
}
|
|
}
|
|
|
|
/** Performs `action` for each element in `list` in order.
|
|
@param[action] It makes a double of the next node, so it can be to delete the
|
|
element and even assign it's values null.
|
|
@order \Theta(|`list`|) \times O(`action`) @allow */
|
|
static void L_(list_for_each)(struct L_(list) *const list,
|
|
const PL_(action_fn) action) {
|
|
struct L_(listlink) *x, *next_x;
|
|
assert(list && action);
|
|
for(x = list->u.flat.next; next_x = x->next; x = next_x) action(x);
|
|
}
|
|
|
|
/** Iterates through `list` and calls `predicate` until it returns true.
|
|
@return The first `predicate` that returned true, or, if the statement is
|
|
false on all, null.
|
|
@order \O(|`list`|) \times \O(`predicate`) @allow */
|
|
static struct L_(listlink) *L_(list_any)(const struct L_(list) *const list,
|
|
const PL_(predicate_fn) predicate) {
|
|
struct L_(listlink) *link, *next_link;
|
|
assert(list && predicate);
|
|
for(link = list->u.flat.next; next_link = link->next; link = next_link)
|
|
if(predicate(link)) return link;
|
|
return 0;
|
|
}
|
|
|
|
/** Corrects `list` ends to compensate for memory relocation of the list
|
|
itself. (Can only have one copy of the list, this will invalidate all other
|
|
copies.) @order \Theta(1) @allow */
|
|
static void L_(list_self_correct)(struct L_(list) *const list) {
|
|
assert(list && !list->u.flat.zero);
|
|
if(!list->u.flat.next->next) { /* Empty. */
|
|
assert(!list->u.flat.prev->prev);
|
|
list->u.flat.next = &list->u.as_tail.tail;
|
|
list->u.flat.prev = &list->u.as_head.head;
|
|
} else { /* Non-empty. */
|
|
list->u.flat.prev->next = &list->u.as_tail.tail;
|
|
list->u.flat.next->prev = &list->u.as_head.head;
|
|
}
|
|
}
|
|
|
|
/* <!-- iterate interface */
|
|
|
|
/* Contains all iteration parameters. (Since this is a permutation, the
|
|
iteration is defined by none other then itself. Used for traits.) */
|
|
struct PL_(iterator) { struct L_(listlink) *link; };
|
|
|
|
/** Loads `list` into `it`. @implements begin */
|
|
static void PL_(begin)(struct PL_(iterator) *const it,
|
|
const struct L_(list) *const list)
|
|
{ assert(it && list), it->link = L_(list_head)(list); }
|
|
|
|
/** Advances `it`. @implements next */
|
|
static const struct L_(listlink) *PL_(next)(struct PL_(iterator) *const it) {
|
|
struct L_(listlink) *here = it->link;
|
|
assert(it);
|
|
if(!here) return 0;
|
|
it->link = L_(list_next)(it->link);
|
|
return here;
|
|
}
|
|
|
|
/* iterate --> */
|
|
|
|
/* <!-- box (multiple traits) */
|
|
#define BOX_ PL_
|
|
#define BOX_CONTAINER struct L_(list)
|
|
#define BOX_CONTENTS struct L_(listlink)
|
|
|
|
#ifdef LIST_TEST /* <!-- test */
|
|
/* Forward-declare. */
|
|
static void (*PL_(to_string))(const struct L_(listlink) *, char (*)[12]);
|
|
static const char *(*PL_(list_to_string))(const struct L_(list) *);
|
|
#include "../test/test_list.h"
|
|
#endif /* test --> */
|
|
|
|
static void PL_(unused_base_coda)(void);
|
|
static void PL_(unused_base)(void) {
|
|
L_(list_head)(0); L_(list_tail)(0); L_(list_previous)(0); L_(list_next)(0);
|
|
L_(list_clear)(0); L_(list_add_before)(0, 0); L_(list_add_after)(0, 0);
|
|
L_(list_unshift)(0, 0); L_(list_push)(0, 0); L_(list_remove)(0);
|
|
L_(list_shift)(0); L_(list_pop)(0); L_(list_to)(0, 0);
|
|
L_(list_to_before)(0, 0); L_(list_to_if)(0, 0, 0); L_(list_for_each)(0, 0);
|
|
L_(list_any)(0, 0); L_(list_self_correct)(0);
|
|
PL_(begin)(0, 0); PL_(next)(0); PL_(unused_base_coda)();
|
|
}
|
|
static void PL_(unused_base_coda)(void) { PL_(unused_base)(); }
|
|
|
|
|
|
#elif defined(LIST_TO_STRING) /* base code --><!-- to string trait */
|
|
|
|
|
|
#ifdef LIST_TO_STRING_NAME /* <!-- name */
|
|
#define SZ_(n) LIST_CAT(L_(list), LIST_CAT(LIST_TO_STRING_NAME, n))
|
|
#else /* name --><!-- !name */
|
|
#define SZ_(n) LIST_CAT(L_(list), n)
|
|
#endif /* !name --> */
|
|
#define TO_STRING LIST_TO_STRING
|
|
#include "to_string.h" /** \include */
|
|
#ifdef LIST_TEST /* <!-- expect: greedy satisfy forward-declared. */
|
|
#undef LIST_TEST
|
|
static PSZ_(to_string_fn) PL_(to_string) = PSZ_(to_string);
|
|
static const char *(*PL_(list_to_string))(const struct L_(list) *)
|
|
= &SZ_(to_string);
|
|
#endif /* expect --> */
|
|
#undef SZ_
|
|
#undef LIST_TO_STRING
|
|
#ifdef LIST_TO_STRING_NAME
|
|
#undef LIST_TO_STRING_NAME
|
|
#endif
|
|
|
|
|
|
#else /* to string trait --><!-- compare trait */
|
|
|
|
|
|
#ifdef LIST_COMPARE_NAME /* <!-- name */
|
|
#define LC_(n) LIST_CAT(L_(list), LIST_CAT(LIST_COMPARE_NAME, n))
|
|
#else /* name --><!-- !name */
|
|
#define LC_(n) LIST_CAT(L_(list), n)
|
|
#endif /* !name --> */
|
|
#include "list_coda.h" /** \include */
|
|
#ifdef LIST_TEST /* <!-- test: this detects and outputs compare test. */
|
|
#include "../test/test_list.h"
|
|
#endif /* test --> */
|
|
#undef LC_
|
|
#ifdef LIST_COMPARE_NAME
|
|
#undef LIST_COMPARE_NAME
|
|
#endif
|
|
#ifdef LIST_COMPARE
|
|
#undef LIST_COMPARE
|
|
#endif
|
|
#ifdef LIST_IS_EQUAL
|
|
#undef LIST_IS_EQUAL
|
|
#endif
|
|
|
|
|
|
#endif /* traits --> */
|
|
|
|
|
|
#ifdef LIST_EXPECT_TRAIT /* <!-- trait */
|
|
#undef LIST_EXPECT_TRAIT
|
|
#else /* trait --><!-- !trait */
|
|
#ifdef LIST_TEST
|
|
#error No LIST_TO_STRING traits defined for LIST_TEST.
|
|
#endif
|
|
#undef LIST_NAME
|
|
#undef BOX_
|
|
#undef BOX_CONTAINER
|
|
#undef BOX_CONTENTS
|
|
#undef LIST_BASE
|
|
/* box (multiple traits) --> */
|
|
#endif /* !trait --> */
|
|
#undef LIST_TO_STRING_TRAIT
|
|
#undef LIST_COMPARE_TRAIT
|
|
#undef LIST_TRAITS
|