This commit is contained in:
Neil 2022-07-06 10:02:28 -07:00
parent 4122fc3407
commit 78cdeafc8b
9 changed files with 960 additions and 2201 deletions

View File

@ -110,90 +110,67 @@ static int PA_(is_element_c)(PA_(type_c) *const x) { return !!x; }
/* Enumerate the contents (`input_or_output_const_iterator`.)
@implements `forward` */
struct PA_(forward) { const struct A_(array) *a; size_t next; };
/** @return Before `a`. @implements `forward_begin` */
static struct PA_(forward) PA_(forward_begin)(const struct A_(array) *const a) {
/** @return A pointer to null in `a`. @implements `forward` */
static struct PA_(forward) PA_(forward)(const struct A_(array) *const a) {
struct PA_(forward) it; it.a = a, it.next = 0; return it; }
/** Move to next `it`. @return Element or null. @implements `forward_next` */
static PA_(type_c) *PA_(forward_next)(struct PA_(forward) *const it)
{ return assert(it), it->a && it->next < it->a->size
? it->a->data + it->next++ : 0; }
/** Move to next `it`. @return Element or null. @implements `next_c` */
static PA_(type_c) *PA_(next_c)(struct PA_(forward) *const it) {
assert(it);
if(it->a && it->next < it->a->size) return it->a->data + it->next++;
else { it->next = 0; return 0; }
}
#define BOX_ITERATOR PA_(type) *
/** Is `x` not null? @implements `is_element` */
static int PA_(is_element)(const PA_(type) *const x) { return !!x; }
/* More complex iterator that supports bi-directional movement and write. The
cursor is half way between elements, `cur = cursor - 0.5`, pointing left
(`dir` false) or right (`dir` true). @implements `iterator` */
struct PA_(iterator) { struct A_(array) *a; size_t cur; int dir; };
/** @return Before `a`. @implements `begin` */
static struct PA_(iterator) PA_(begin)(struct A_(array) *const a)
{ struct PA_(iterator) it; it.a = a, it.cur = 0, it.dir = 0; return it; }
/** After `a`. @implements `end` */
static struct PA_(iterator) PA_(end)(struct A_(array) *const a)
{ struct PA_(iterator) it; it.a = a, it.cur = a ? a->size : 0, it.dir = 1;
return it; }
/** Move to next `it`. @return Element or null. @implements `next` */
static PA_(type) *PA_(next)(struct PA_(iterator) *const it) {
size_t size;
PA_(type) *element;
assert(it);
if(!it->a || !(size = it->a->size)) { /* Null or empty. */
it->cur = 0, it->dir = 0, element = 0;
} else if(!it->dir) { /* Left. */
it->dir = 1;
if(it->cur < size) element = it->a->data + it->cur;
else it->cur = size, element = 0; /* Ended by prev-next. */
} else if(size <= it->cur) { /* Right and already ended. */
it->cur = size, element = 0;
} else { /* Right. */
if(++it->cur < size) element = it->a->data + it->cur;
else element = 0; /* Just ended. */
}
return element;
/* @implements `iterator` */
struct PA_(iterator) { struct A_(array) *a; size_t i; int seen; };
/** @return A pointer to null in `a`. @implements `iterator` */
static struct PA_(iterator) PA_(iterator)(struct A_(array) *const a) {
struct PA_(iterator) it; it.a = a, it.i = 0, it.seen = 0;
return it;
}
/** Move to previous `it`. @return Element or null. @implements `previous` */
static PA_(type) *PA_(previous)(struct PA_(iterator) *const it) {
size_t size;
PA_(type) *element;
/** Move to next `it`. @return Element or null on end. @implements `next` */
static PA_(type) *PA_(next)(struct PA_(iterator) *const it) {
size_t i;
assert(it);
if(!it->a || !(size = it->a->size)) { /* Null or empty. */
it->cur = 0, it->dir = 0, element = 0;
} else if(it->dir) { /* Right. */
it->dir = 0;
/* Clip. */
if(size < it->cur) element = it->a->data + (it->cur = size) - 1;
else if(it->cur) element = it->a->data + it->cur - 1;
else element = 0; /* Ended by next-prev. */
} else if(!it->cur) { /* Left and already ended. */
element = 0;
} else { /* Left. */
if(size < it->cur) element = it->a->data + (it->cur = size) - 1;
else if(--it->cur) element = it->a->data + it->cur - 1;
else element = 0; /* Just ended. */
if(!it->a || (i = it->i + !!it->seen) >= it->a->size)
{ *it = PA_(iterator)(it->a); return 0; }
return it->a->data + (it->seen = 1, it->i = i);
}
/** Move to previous `it`. @return Element or null on end.
@implements `previous` */
static PA_(type) *PA_(previous)(struct PA_(iterator) *const it) {
size_t i, size;
assert(it);
if(!it->a || !(size = it->a->size)) goto reset;
if(i = it->i) {
if(i > size) i = size;
i--;
} else {
if(!it->seen) i = it->a->size - 1;
else goto reset;
}
return element;
return it->a->data + (it->seen = 1, it->i = i);
reset:
*it = PA_(iterator)(it->a);
return 0;
}
/** Removes the element last returned by `it`. (Untested.)
@return There was an element. @order \O(`a.size`). @implements `remove` */
static int PA_(remove)(struct PA_(iterator) *const it) {
assert(0 && it);
if(!it->dir || !it->a) return 0;
if(it->dir) {
if(it->a->size <= it->cur) return 0;
} else {
if(!it->cur || it->a->size < it->cur) return 0;
it->cur--;
}
memmove(it->a->data + it->cur, it->a->data + it->cur + 1,
sizeof *it->a->data * (--it->a->size - it->cur));
assert(0 && 1);
if(!it->a || !it->seen || it->a->size <= it->i) return 0;
memmove(it->a->data + it->i, it->a->data + it->i + 1,
sizeof *it->a->data * (--it->a->size - it->i));
return 1;
}
#define BOX_ACCESS
/** @return Iterator immediately before element `idx` of `a`.
@implements `index` */
static struct PA_(iterator) PA_(index)(struct A_(array) *a, size_t idx)
{ struct PA_(iterator) it; it.a = a, it.cur = idx, it.dir = 0; return it; }
@implements `before` */
static struct PA_(iterator) PA_(before)(struct A_(array) *a, size_t idx)
{ struct PA_(iterator) it; it.a = a, it.i = idx, it.seen = 0; return it; }
/** Size of `a`. @implements `size` */
static size_t PA_(size)(const struct A_(array) *a) { return a ? a->size : 0; }
/** @return Element `idx` of `a`. @implements `at` */
@ -223,16 +200,13 @@ static struct A_(array) A_(array)(void)
static void A_(array_)(struct A_(array) *const a)
{ if(a) free(a->data), *a = A_(array)(); }
/** @return A cursor before the front of `a`. */
static struct A_(array_iterator) A_(array_begin)(struct A_(array) *a)
{ struct A_(array_iterator) it; it._ = PA_(begin)(a); return it; }
/** @return An iterator after the end of `a`. */
static struct A_(array_iterator) A_(array_end)(struct A_(array) *a)
{ struct A_(array_iterator) it; it._ = PA_(end)(a); return it; }
/** @return An iterator of `a`. */
static struct A_(array_iterator) A_(array_iterator)(struct A_(array) *a)
{ struct A_(array_iterator) it; it._ = PA_(iterator)(a); return it; }
/** @return An iterator at `idx` of `a`. */
static struct A_(array_iterator) A_(array_index)(struct A_(array) *a,
static struct A_(array_iterator) A_(array_iterator_before)(struct A_(array) *a,
size_t idx) { struct A_(array_iterator) it;
it._ = PA_(index)(a, idx); return it; }
it._ = PA_(before)(a, idx); return it; }
/** @return `it` next element. */
static PA_(type) *A_(array_next)(struct A_(array_iterator) *const it)
{ return assert(it), PA_(next)(&it->_); }
@ -410,11 +384,11 @@ static const char *(*PA_(array_to_string))(const struct A_(array) *);
static void PA_(unused_base_coda)(void);
static void PA_(unused_base)(void) {
PA_(is_element_c)(0); PA_(forward_begin)(0); PA_(forward_next)(0);
PA_(is_element_c)(0); PA_(forward)(0); PA_(next_c)(0);
PA_(is_element)(0); PA_(remove)(0); PA_(size)(0); PA_(at)(0, 0);
PA_(tell_size)(0, 0);
A_(array)(); A_(array_)(0);
A_(array_begin)(0); A_(array_end)(0); A_(array_index)(0, 0);
A_(array_iterator)(0); A_(array_iterator_before)(0, 0);
A_(array_previous)(0); A_(array_next)(0); A_(array_previous)(0);
A_(array_insert)(0, 0, 0); A_(array_new)(0);
A_(array_shrink)(0); A_(array_remove)(0, 0); A_(array_lazy_remove)(0, 0);

View File

@ -13,8 +13,8 @@
#if INT_MAX >= 100000000000
#error int_to_string requires truncation on this compiler.
#endif
static void int_to_string(const int *const n,
char (*const a)[12]) { sprintf(*a, "%d", *n); }
static void int_to_string(const int *const n, char (*const a)[12])
{ sprintf(*a, "%d", *n); }
#define ARRAY_NAME int
#define ARRAY_TYPE int
#define ARRAY_EXPECT_TRAIT
@ -67,7 +67,8 @@ static int leap(int y) {
if(!(y % 4)) return 1;
return 0;
}
/** Not defined for some implementations. C11 */
/** Assumes: reverse ordering of byte-fields; unsigned is defined; C11 and GNU
anonymous unions. */
union date32 {
uint32_t u32;
struct { unsigned day : 5, month : 4, year : 23; };
@ -83,8 +84,7 @@ static union date32 date_to_32(const int y, const int m, const int d) {
union date32 d32 = { 0 };
/* Leap year calculations only work at y>=1 and Gregorian Calendar and max
23 bits. */
if(y < 1582 || y > 8388607 || m < 1 || m > 12 || d < 1 || d > 31)
goto no;
if(y < 1582 || y > 8388607 || m < 1 || m > 12 || d < 1 || d > 31) goto no;
switch(m) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12: break;
case 4: case 6: case 9: case 11: if(d > 30) goto no; break;
@ -104,6 +104,7 @@ static unsigned weekday(union date32 d) {
}
/* Contained in <lex.h> to share with <lex.re_c.c>. */
#define ARRAY_NAME lex
#define ARRAY_TYPE struct lex
#include "array.h"
@ -126,6 +127,97 @@ static void entry_to_string(const struct page_tree_entry_c entry,
struct source { char *key, *desc; };
/*
### plot with steps
reset session
$Data <<EOD
1,1,0
1,2,0
1,3,0
1,4,2
1,5,1
1,6,3
1,7,3
1,8,1
1,9,3
1,10,8
1,11,1
1,12,0
1,13,3
EOD
set title "Cumulative count" font ",16"
set xlabel "episode"
set ylabel "cumulative count"
set xtics 1
set key bottom right
set grid
unset border
set datafile separator comma
plot $Data u 2:($3) smooth cumulative with steps lw 2 lc "red" ti "cumulative count"
### end of code
*/
static int bible_graph(/*const*/ struct page_tree *const journal) {
enum { CHILL, BOOK, CHAPTER, WORD, NEXT } state = CHILL;
struct page_tree_entry entry = { 0, 0 };
struct lex *lex = 0;
size_t count = 0;
for(struct page_tree_iterator p_it = page_tree_iterator(journal);
(entry = page_tree_next(&p_it)).key; ) {
struct page *const page = entry.value;
for(struct lex_array_iterator l_it = lex_array_iterator(&page->lexx);
(lex = lex_array_next(&l_it)); ) {
switch(lex->symbol) {
case BIBLE_BOOK:
if(state != CHILL && state != WORD) goto catch;
if(state == WORD) printf("\n");
fprintf(stderr, "%d-%.2d-%.2d: %.*s ",
entry.key->year, entry.key->month, entry.key->day,
(int)(lex->s1 - lex->s0), lex->s0);
state = BOOK;
break;
case BIBLE_CHAPTER_VERSE:
if(state != BOOK) goto catch;
printf("%.*s -- \"", (int)(lex->s1 - lex->s0), lex->s0);
state = CHAPTER;
break;
case BIBLE_NEXT:
if(state != WORD) goto catch;
printf("\"\n");
break;
case BIBLE_TEXT:
if(state != WORD && state != CHAPTER && state != NEXT)
goto catch;
printf("%s%.*s", state == WORD ? "*" : "",
(int)(lex->s1 - lex->s0), lex->s0);
count++;
state = WORD;
break;
default:
if(state != CHILL && state != WORD) goto catch;
if(state == WORD) printf("\"\n"), state = CHILL;
break;
}
}
if(state != CHILL && state != WORD) goto catch;
if(state == WORD) printf("\n"), state = CHILL;
}
printf("Count: %lu.\n", (unsigned long)count);
return 1;
catch:
fprintf(stderr, "Bible error.\n");
if(entry.key) {
fprintf(stderr, "On date: %d-%.2d-%.2d.\n",
entry.key->year, entry.key->month, entry.key->day);
if(lex) fprintf(stderr, "At line %lu.\n", (unsigned long)lex->line);
}
errno = EILSEQ;
return 0;
}
int main(int argc, char **argv) {
int success = EXIT_SUCCESS;
char *intent = 0;
@ -157,7 +249,7 @@ int main(int argc, char **argv) {
closedir(dir), dir = 0;
/* Sort the years for sensible ordering of parsing. */
qsort(years.data, years.size, sizeof *years.data, &void_int_cmp);
fprintf(stderr, "(Files in %s: %s.)\n", argv[1], int_array_to_string(&years));
fprintf(stderr, "(In %s: %s.)\n", argv[1], int_array_to_string(&years));
/* Go though each year. */
for(y = years.data, y_end = y + years.size; y < y_end; y++) {
@ -178,8 +270,7 @@ int main(int argc, char **argv) {
}
closedir(dir), dir = 0;
qsort(months.data, months.size, sizeof *months.data, &void_int_cmp);
fprintf(stderr, "(Files in %s: %s.)\n",
fn, int_array_to_string(&months));
fprintf(stderr, "(In %s: %s.)\n", fn, int_array_to_string(&months));
/* Go though each month. */
for(m = months.data, m_end = m + months.size; m < m_end; m++) {
@ -200,8 +291,7 @@ int main(int argc, char **argv) {
}
closedir(dir), dir = 0;
qsort(days.data, days.size, sizeof *days.data, &void_int_cmp);
fprintf(stderr, "(Files in %s: %s.)\n",
fn, int_array_to_string(&days));
fprintf(stderr, "(In %s: %s.)\n", fn, int_array_to_string(&days));
for(d = days.data, d_end = d + days.size; d < d_end; d++) {
struct lex *lex = 0;
@ -251,11 +341,16 @@ syntax:
int_array_clear(&months);
if(chdir("..") == -1) goto catch;
//break; /* fixme */
/* fixme: Expand, contact is the next thing that it doesn't get. */
if(*y == 1996) break;
}
page_tree_bulk_finish(&journal);
int_array_(&years), int_array_(&months), int_array_(&days);
printf("Journal has entries: %s\n", page_tree_to_string(&journal));
fprintf(stderr, "Journal has entries: %s\n", page_tree_to_string(&journal));
/* Do something interesting? */
if(!bible_graph(&journal)) goto catch;
goto finally;
catch:
success = EXIT_FAILURE;
@ -265,12 +360,12 @@ finally:
if(dir && closedir(dir)) success = EXIT_FAILURE, perror("dir");
int_array_(&years), int_array_(&months), int_array_(&days);
struct page_tree_entry entry;
for(struct page_tree_iterator it = page_tree_begin(&journal);
for(struct page_tree_iterator it = page_tree_iterator(&journal);
(entry = page_tree_next(&it)).key; ) {
struct page *const page = entry.value;
char z[12];
date32_to_string(*entry.key, &z);
printf("Freeing %s.\n", z);
/*printf("Freeing %s.\n", z);*/
lex_array_(&page->lexx);
char_array_(&page->entry);
}

View File

@ -15,7 +15,7 @@ int lex_looks_like_day(const char *);
/* Edicts. */ \
X(SOURCE), X(DEFAULT), X(SOURCE_RECALL), \
X(LOCATION), X(LOCATION_SAVE), X(LOCATION_RECALL), \
X(SIGNIFICANT), X(SIGNIFICANT_RECALL), \
X(SIGNIFICANT), X(SIGNIFICANT_RECALL), X(EDITORIALIZING), \
\
/* Arguments. */ \
X(ARG_KEYWORD), X(ARG_DATE), X(ARG_NATURAL), X(ARG_FREEFORM), \

View File

@ -216,6 +216,14 @@ scan:
scan.is_ws_expected = 1, scan.is_source = 0;
return x->symbol = DEFAULT, 1; }
<edict> "ed"
{ if(scan.is_ws_expected || scan.edict.size)
return x->symbol = SYNTAX, 0;
scan.is_ws_expected = 1; /* no idea, just copy; probably should do sth */
scan.edict.size = 1;
scan.edict.expect[0] = EXPECT_FREEFORM;
return x->symbol = EDITORIALIZING, 1; }
<edict> "significant"
{ if(scan.is_ws_expected || scan.edict.size)
return x->symbol = SYNTAX, 0;

View File

@ -1,434 +0,0 @@
/** @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

View File

@ -1,378 +0,0 @@
/** @license 2021 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
@abstract Source <src/pool.h>, depends on <src/heap.h>, and <src/array.h>;
examples <test/test_pool.c>.
@subtitle Stable pool
![Example of Pool](../web/pool.png)
<tag:<P>pool> is a memory pool that stores <typedef:<PP>type>. Pointers to
valid items in the pool are stable, but not generally in any order. When
removal is ongoing and uniformly sampled while reaching a steady-state size,
it will eventually settle in one contiguous region.
@param[POOL_NAME, POOL_TYPE]
`<P>` that satisfies `C` naming conventions when mangled and a valid tag type,
<typedef:<PP>type>, associated therewith; required. `<PP>` is private, whose
names are prefixed in a manner to avoid collisions.
@param[POOL_CHUNK_MIN_CAPACITY]
Default is 8; optional number in
`[2, (SIZE_MAX - sizeof pool_chunk) / sizeof <PP>type]` that the capacity can
not go below.
@param[POOL_EXPECT_TRAIT]
Do not un-define certain variables for subsequent inclusion in a trait.
@param[POOL_TO_STRING_NAME, POOL_TO_STRING]
To string trait contained in <to_string.h>; `<PSZ>` that satisfies `C` naming
conventions when mangled and function implementing <typedef:<PSZ>to_string_fn>.
There can be multiple to string traits, but only one can omit
`POOL_TO_STRING_NAME`. This container is only partially iterable: the values
are only the first chunk, so this is not very useful except for debugging.
@depend [array](https://github.com/neil-edelman/array)
@depend [heap](https://github.com/neil-edelman/heap)
@std C89 */
#if !defined(POOL_NAME) || !defined(POOL_TYPE)
#error Name POOL_NAME undefined or tag type POOL_TYPE undefined.
#endif
#if defined(POOL_TO_STRING_NAME) || defined(POOL_TO_STRING)
#define POOL_TO_STRING_TRAIT 1
#else
#define POOL_TO_STRING_TRAIT 0
#endif
#define POOL_TRAITS POOL_TO_STRING_TRAIT
#if POOL_TRAITS > 1
#error Only one trait per include is allowed; use POOL_EXPECT_TRAIT.
#endif
#if defined(POOL_TO_STRING_NAME) && !defined(POOL_TO_STRING)
#error POOL_TO_STRING_NAME requires POOL_TO_STRING.
#endif
#ifndef POOL_H /* <!-- idempotent */
#define POOL_H
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#if defined(POOL_CAT_) || defined(POOL_CAT) || defined(P_) || defined(PP_) \
|| defined(POOL_IDLE)
#error Unexpected defines.
#endif
/* <Kernighan and Ritchie, 1988, p. 231>. */
#define POOL_CAT_(n, m) n ## _ ## m
#define POOL_CAT(n, m) POOL_CAT_(n, m)
#define P_(n) POOL_CAT(POOL_NAME, n)
#define PP_(n) POOL_CAT(pool, P_(n))
#define POOL_IDLE { ARRAY_IDLE, HEAP_IDLE, (size_t)0 }
/** @return An order on `a`, `b` which specifies a max-heap. */
static int pool_index_compare(const size_t a, const size_t b) { return a < b; }
#define HEAP_NAME poolfree
#define HEAP_TYPE size_t
#define HEAP_COMPARE &pool_index_compare
#include "heap.h"
#endif /* idempotent --> */
#if POOL_TRAITS == 0 /* <!-- base code */
#ifndef POOL_CHUNK_MIN_CAPACITY /* <!-- !min */
#define POOL_CHUNK_MIN_CAPACITY 8
#endif /* !min --> */
#if POOL_CHUNK_MIN_CAPACITY < 2
#error Pool chunk capacity error.
#endif
/** A valid tag type set by `POOL_TYPE`. */
typedef POOL_TYPE PP_(type);
/* Size and chunk, which goes into a sorted array. */
struct PP_(slot) { size_t size; PP_(type) *chunk; };
#define ARRAY_NAME PP_(slot)
#define ARRAY_TYPE struct PP_(slot)
#include "array.h"
/** Consists of a map of several chunks of increasing size and a free-heap.
Zeroed data is a valid state. To instantiate to an idle state, see
<fn:<P>pool>, `POOL_IDLE`, `{0}` (`C99`,) or being `static`.
![States.](../web/states.png) */
struct P_(pool) {
struct PP_(slot_array) slots;
struct poolfree_heap free0; /* Free-list in chunk-zero. */
size_t capacity0; /* Capacity of chunk-zero. */
};
/** @return Index of sorted chunk that is higher than `x` in `slots`, but
treating zero as special. @order \O(\log `chunks`) */
static size_t PP_(upper)(const struct PP_(slot_array) *const slots,
const PP_(type) *const x) {
const struct PP_(slot) *const base = slots->data;
size_t n, b0, b1;
assert(slots && x);
if(!(n = slots->size)) return 0;
assert(base);
if(!--n) return 1;
/* The last one is a special case: it doesn't have an upper bound. */
for(b0 = 1, --n; n; n /= 2) {
b1 = b0 + n / 2;
if(x < base[b1].chunk) { continue; }
else if(base[b1 + 1].chunk <= x) { b0 = b1 + 1; n--; continue; }
else { return b1 + 1; }
}
return b0 + (x >= base[slots->size - 1].chunk);
}
/** Which chunk is `x` in `pool`?
@order \O(\log `chunks`), \O(\log \log `size`)? */
static size_t PP_(chunk_idx)(const struct P_(pool) *const pool,
const PP_(type) *const x) {
struct PP_(slot) *const base = pool->slots.data;
size_t up;
assert(pool && pool->slots.size && base && x);
/* One chunk, assume it's in that chunk; first chunk is `capacity0`. */
if(pool->slots.size <= 1
|| (x >= base[0].chunk && x < base[0].chunk + pool->capacity0))
return assert(x >= base[0].chunk && x < base[0].chunk + pool->capacity0),
0;
up = PP_(upper)(&pool->slots, x);
return assert(up), up - 1;
}
/** Makes sure there are space for `n` further items in `pool`.
@return Success. */
static int PP_(buffer)(struct P_(pool) *const pool, const size_t n) {
const size_t min_size = POOL_CHUNK_MIN_CAPACITY,
max_size = (size_t)-1 / sizeof(PP_(type));
struct PP_(slot) *base = pool->slots.data, *slot;
PP_(type) *chunk;
size_t c, insert;
int is_recycled = 0;
assert(pool && min_size <= max_size && pool->capacity0 <= max_size &&
(!pool->slots.size && !pool->free0.a.size /* !chunks[0] -> !free0 */
|| pool->slots.size && base
&& base[0].size <= pool->capacity0
&& (!pool->free0.a.size
|| pool->free0.a.size < base[0].size
&& pool->free0.a.data[0] < base[0].size)));
/* Ensure space for new slot. */
if(!n || pool->slots.size && n <= pool->capacity0
- base[0].size + pool->free0.a.size) return 1; /* Already enough. */
if(max_size < n) return errno = ERANGE, 1; /* Request unsatisfiable. */
if(!PP_(slot_array_buffer)(&pool->slots, 1)) return 0;
base = pool->slots.data; /* It may have moved! */
/* Figure out the capacity of the next chunk. */
c = pool->capacity0;
if(pool->slots.size && base[0].size) { /* ~Golden ratio. */
size_t c1 = c + (c >> 1) + (c >> 3);
c = (c1 < c || c1 > max_size) ? max_size : c1;
}
if(c < min_size) c = min_size;
if(c < n) c = n;
/* Allocate it. */
if(pool->slots.size && !base[0].size)
is_recycled = 1, chunk = realloc(base[0].chunk, c * sizeof *chunk);
else chunk = malloc(c * sizeof *chunk);
if(!chunk) { if(!errno) errno = ERANGE; return 0; }
pool->capacity0 = c; /* We only need to store the capacity of chunk 0. */
if(is_recycled) return base[0].size = 0, base[0].chunk = chunk, 1;
/* Evict chunk 0. */
if(!pool->slots.size) insert = 0;
else insert = PP_(upper)(&pool->slots, base[0].chunk);
assert(insert <= pool->slots.size);
slot = PP_(slot_array_insert)(&pool->slots, 1, insert);
assert(slot); /* Made space for it before. */
slot->chunk = base[0].chunk, slot->size = base[0].size;
base[0].chunk = chunk, base[0].size = 0;
return 1;
}
/** Either `data` in `pool` is in a secondary chunk, in which case it
decrements the size, or it's the zero-chunk, where it gets added to the
free-heap.
@return Success. It may fail due to a free-heap memory allocation error.
@order Amortized \O(\log \log `items`) @throws[realloc] */
static int PP_(remove)(struct P_(pool) *const pool,
const PP_(type) *const data) {
size_t c = PP_(chunk_idx)(pool, data);
struct PP_(slot) *slot = pool->slots.data + c;
assert(pool && pool->slots.size && data);
if(!c) { /* It's in the zero-slot, we need to deal with the free-heap. */
const size_t idx = (size_t)(data - slot->chunk);
assert(pool->capacity0 && slot->size <= pool->capacity0
&& idx < slot->size);
if(idx + 1 == slot->size) {
/* Keep shrinking going while item on the free-heap are exposed. */
while(--slot->size && !poolfree_heap_is_empty(&pool->free0)) {
const size_t free = poolfree_heap_peek(&pool->free0);
if(free < slot->size - 1) break;
assert(free == slot->size - 1);
poolfree_heap_pop(&pool->free0);
}
} else if(!poolfree_heap_add(&pool->free0, idx)) return 0;
} else if(assert(slot->size), !--slot->size) {
PP_(type) *const chunk = slot->chunk;
PP_(slot_array_remove)(&pool->slots, pool->slots.data + c);
free(chunk);
}
return 1;
}
/** Initializes `pool` to idle. @order \Theta(1) @allow */
static void P_(pool)(struct P_(pool) *const pool) { assert(pool),
PP_(slot_array)(&pool->slots), poolfree_heap(&pool->free0),
pool->capacity0 = 0; }
/** Destroys `pool` and returns it to idle. @order \O(\log `data`) @allow */
static void P_(pool_)(struct P_(pool) *const pool) {
struct PP_(slot) *s, *s_end;
assert(pool);
for(s = pool->slots.data, s_end = s + pool->slots.size; s < s_end; s++)
assert(s->chunk), free(s->chunk);
PP_(slot_array_)(&pool->slots);
poolfree_heap_(&pool->free0);
P_(pool)(pool);
}
/** Ensure capacity of at least `n` further items in `pool`. Pre-sizing is
better for contiguous blocks, but takes up that memory.
@return Success. @throws[ERANGE, malloc] @allow */
static int P_(pool_buffer)(struct P_(pool) *const pool, const size_t n) {
return assert(pool), PP_(buffer)(pool, n);
}
/** This pointer is constant until it gets <fn:<P>pool_remove>.
@return A pointer to a new uninitialized element from `pool`.
@throws[ERANGE, malloc] @order amortised O(1) @allow */
static PP_(type) *P_(pool_new)(struct P_(pool) *const pool) {
struct PP_(slot) *slot0;
assert(pool);
if(!PP_(buffer)(pool, 1)) return 0;
assert(pool->slots.size && (pool->free0.a.size ||
pool->slots.data[0].size < pool->capacity0));
if(!poolfree_heap_is_empty(&pool->free0)) {
/* Cheating: we prefer the minimum index from a max-heap, but it
doesn't really matter, so take the one off the array used for heap. */
size_t *free;
free = heap_poolfree_node_array_pop(&pool->free0.a);
return assert(free), pool->slots.data[0].chunk + *free;
}
/* The free-heap is empty; guaranteed by <fn:<PP>buffer>. */
slot0 = pool->slots.data + 0;
assert(slot0 && slot0->size < pool->capacity0);
return slot0->chunk + slot0->size++;
}
/** Deletes `data` from `pool`. Do not remove data that is not in `pool`.
@return Success. @order \O(\log \log `items`) @allow */
static int P_(pool_remove)(struct P_(pool) *const pool,
PP_(type) *const data) { return PP_(remove)(pool, data); }
/** Removes all from `pool`, but keeps it's active state, only freeing the
smaller blocks. @order \O(\log `items`) @allow */
static void P_(pool_clear)(struct P_(pool) *const pool) {
struct PP_(slot) *s, *s_end;
assert(pool);
if(!pool->slots.size) { assert(!pool->free0.a.size); return; }
for(s = pool->slots.data + 1, s_end = s - 1 + pool->slots.size;
s < s_end; s++) assert(s->chunk && s->size), free(s->chunk);
pool->slots.data[0].size = 0;
pool->slots.size = 1;
poolfree_heap_clear(&pool->free0);
}
/* <!-- iterate interface: it's not actually possible to iterate though given
the information that we have, but placing it here for testing purposes.
Iterates through the zero-slot, ignoring the free list. Do not call. */
struct PP_(iterator);
struct PP_(iterator) { struct PP_(slot) *slot0; size_t i; };
/** Loads `pool` into `it`. @implements begin */
static void PP_(begin)(struct PP_(iterator) *const it,
const struct P_(pool) *const pool) {
assert(it && pool);
if(pool->slots.size) it->slot0 = pool->slots.data + 0;
else it->slot0 = 0;
it->i = 0;
}
/** Advances `it`. @implements next */
static const PP_(type) *PP_(next)(struct PP_(iterator) *const it) {
assert(it);
return it->slot0 && it->i < it->slot0->size
? it->slot0->chunk + it->i++ : 0;
}
/* iterate --> */
#ifdef POOL_TEST /* <!-- test */
/* Forward-declare. */
static void (*PP_(to_string))(const PP_(type) *, char (*)[12]);
static const char *(*PP_(pool_to_string))(const struct P_(pool) *);
#include "../test/test_pool.h"
#endif /* test --> */
/* <!-- box (multiple traits) */
#define BOX_ PP_
#define BOX_CONTAINER struct P_(pool)
#define BOX_CONTENTS PP_(type)
static void PP_(unused_base_coda)(void);
static void PP_(unused_base)(void) {
P_(pool)(0); P_(pool_)(0); P_(pool_buffer)(0, 0); P_(pool_new)(0);
P_(pool_remove)(0, 0); P_(pool_clear)(0); PP_(begin)(0, 0);
PP_(next)(0); PP_(unused_base_coda)();
}
static void PP_(unused_base_coda)(void) { PP_(unused_base)(); }
#elif defined(POOL_TO_STRING) /* base code --><!-- to string trait */
#ifdef POOL_TO_STRING_NAME /* <!-- name */
#define SZ_(n) POOL_CAT(P_(pool), POOL_CAT(POOL_TO_STRING_NAME, n))
#else /* name --><!-- !name */
#define SZ_(n) POOL_CAT(P_(pool), n)
#endif /* !name --> */
#define TO_STRING POOL_TO_STRING
#include "to_string.h" /** \include */
#ifdef POOL_TEST /* <!-- expect: greedy satisfy forward-declared. */
#undef POOL_TEST
static PSZ_(to_string_fn) PP_(to_string) = PSZ_(to_string);
static const char *(*PP_(pool_to_string))(const struct P_(pool) *)
= &SZ_(to_string);
#endif /* expect --> */
#undef SZ_
#undef POOL_TO_STRING
#ifdef POOL_TO_STRING_NAME
#undef POOL_TO_STRING_NAME
#endif
#endif /* traits --> */
#ifdef POOL_EXPECT_TRAIT /* <!-- trait */
#undef POOL_EXPECT_TRAIT
#else /* trait --><!-- !trait */
#ifdef POOL_TEST
#error No POOL_TO_STRING traits defined for POOL_TEST.
#endif
#undef POOL_NAME
#undef POOL_TYPE
#undef BOX_
#undef BOX_CONTAINER
#undef BOX_CONTENTS
/* box (multiple traits) --> */
#endif /* !trait --> */
#undef POOL_TO_STRING_TRAIT
#undef POOL_TRAITS

View File

@ -1,988 +0,0 @@
/** @license 2019 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
@abstract Source <src/table.h>; examples <test/test_table.c>.
@subtitle Hash table
![Example of <string>table.](../web/table.png)
<tag:<N>table> implements a set or map of <typedef:<PN>entry> as a hash table.
It must be supplied a <typedef:<PN>hash_fn> and, <typedef:<PN>is_equal_fn> or
<typedef:<PN>inverse_hash_fn>.
@param[TABLE_NAME, TABLE_KEY]
`<N>` that satisfies `C` naming conventions when mangled and a valid
<typedef:<PN>key> associated therewith; required. `<PN>` is private, whose
names are prefixed in a manner to avoid collisions.
@param[TABLE_HASH, TABLE_IS_EQUAL, TABLE_INVERSE]
`TABLE_HASH`, and either `TABLE_IS_EQUAL` or `TABLE_INVERSE`, but not both,
are required. Function satisfying <typedef:<PN>hash_fn>, and
<typedef:<PN>is_equal_fn> or <typedef:<PN>inverse_hash_fn>.
@param[TABLE_VALUE]
An optional type that is the payload of the key, thus making this a map or
associative array. (If the key is part of an aggregate pointer, it will be
more efficient and robust to use a set with a type conversion instead of
storing related pointers in a map.)
@param[TABLE_UINT]
This is <typedef:<PN>uint>, the unsigned type of hash hash of the key given by
<typedef:<PN>hash_fn>; defaults to `size_t`.
@param[TABLE_EXPECT_TRAIT]
Do not un-define certain variables for subsequent inclusion in a trait.
@param[TABLE_DEFAULT_NAME, TABLE_DEFAULT]
Default trait; a name that satisfies `C` naming conventions when mangled and a
<typedef:<PN>value> used in <fn:<N>table<D>get>. There can be multiple
defaults, but only one can omit `TABLE_DEFAULT_NAME`.
@param[TABLE_TO_STRING_NAME, TABLE_TO_STRING]
To string trait contained in <to_string.h>; `<SZ>` that satisfies `C` naming
conventions when mangled and function implementing
<typedef:<PSZ>to_string_fn>. There can be multiple to string traits, but only
one can omit `TABLE_TO_STRING_NAME`.
@std C89 */
#if !defined(TABLE_NAME) || !defined(TABLE_KEY) || !defined(TABLE_HASH) \
|| !(defined(TABLE_IS_EQUAL) ^ defined(TABLE_INVERSE))
#error Name TABLE_NAME, tag type TABLE_KEY, functions TABLE_HASH, and, \
TABLE_IS_EQUAL or TABLE_INVERSE (but not both) undefined.
#endif
#if defined(TABLE_DEFAULT_NAME) || defined(TABLE_DEFAULT)
#define TABLE_DEFAULT_TRAIT 1
#else
#define TABLE_DEFAULT_TRAIT 0
#endif
#if defined(TABLE_TO_STRING_NAME) || defined(TABLE_TO_STRING)
#define TABLE_TO_STRING_TRAIT 1
#else
#define TABLE_TO_STRING_TRAIT 0
#endif
#define TABLE_TRAITS TABLE_DEFAULT_TRAIT + TABLE_TO_STRING_TRAIT
#if TABLE_TRAITS > 1
#error Only one trait per include is allowed; use TABLE_EXPECT_TRAIT.
#endif
#if defined(TABLE_DEFAULT_NAME) && !defined(TABLE_DEFAULT)
#error TABLE_DEFAULT_NAME requires TABLE_DEFAULT.
#endif
#if defined(TABLE_TO_STRING_NAME) && !defined(TABLE_TO_STRING)
#error TABLE_TO_STRING_NAME requires TABLE_TO_STRING.
#endif
#ifndef TABLE_H /* <!-- idempotent */
#define TABLE_H
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#if defined(TABLE_CAT_) || defined(TABLE_CAT) || defined(N_) || defined(PN_) \
|| defined(TABLE_IDLE)
#error Unexpected defines.
#endif
/* <Kernighan and Ritchie, 1988, p. 231>. */
#define TABLE_CAT_(n, m) n ## _ ## m
#define TABLE_CAT(n, m) TABLE_CAT_(n, m)
#define N_(n) TABLE_CAT(TABLE_NAME, n)
#define PN_(n) TABLE_CAT(table, N_(n))
#define TABLE_IDLE { 0, 0, 0, 0 }
/* Use the sign bit to store out-of-band flags when a <typedef:<PN>uint>
represents an address in the table, (such that range of an index is one bit
less.) Choose representations that probably save power. <