Removed the directory now that it's duplicated in interpet.

This commit is contained in:
Neil 2022-12-27 13:59:47 -08:00
parent 8a3718b840
commit b73194d47d
5 changed files with 0 additions and 2146 deletions

View File

@ -1,242 +0,0 @@
# GNU Make 3.81; MacOSX gcc 4.2.1; clang 19.6.0; MacOSX MinGW 4.3.0
# https://stackoverflow.com/questions/18136918/how-to-get-current-relative-directory-of-your-makefile
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
project := $(current_dir)
# dirs
src := src
test := test
build := build
bin := bin
backup := backup
doc := doc
media := media
#lemon := lemon
PREFIX := /usr/local
# files in $(bin)
install := $(project)-`date +%Y-%m-%d`
# extra stuff we should back up
extra :=
# John Graham-Cumming: rwildcard is a recursive wildcard
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) \
$(filter $(subst *,%,$2),$d))
java_srcs := $(call rwildcard, $(src), *.java)
all_c_srcs := $(call rwildcard, $(src), *.c)
c_re_srcs := $(call rwildcard, $(src), *.re.c)
c_rec_srcs := $(call rwildcard, $(src), *.re_c.c)
c_gperf_srcs := $(call rwildcard, $(src), *.gperf.c)
c_srcs := $(filter-out $(c_re_srcs) $(c_rec_srcs) $(c_gperf_srcs), $(all_c_srcs))
h_srcs := $(call rwildcard, $(src), *.h)
y_srcs := $(call rwildcard, $(src), *.y)
all_c_tests := $(call rwildcard, $(test), *.c)
c_re_tests := $(call rwildcard, $(test), *.re.c)
c_rec_tests := $(call rwildcard, $(test), *.re_c.c)
c_tests := $(filter-out $(c_re_tests) $(c_rec_tests), $(all_c_tests))
h_tests := $(call rwildcard, $(test), *.h)
icons := $(call rwildcard, $(media), *.ico)
# combinations
all_h := $(h_srcs) $(h_tests)
all_srcs := $(java_srcs) $(all_c_srcs) $(y_srcs)
all_tests := $(all_c_tests)
all_icons := $(icons)
java_class := $(patsubst $(src)/%.java, $(build)/%.class, $(java_srcs))
c_objs := $(patsubst $(src)/%.c, $(build)/%.o, $(c_srcs))
# must not conflict, eg, foo.c.re and foo.c would go to the same thing
c_re_builds := $(patsubst $(src)/%.re.c, $(build)/%.c, $(c_re_srcs))
c_re_test_builds := $(patsubst $(test)/%.re.c, $(build)/$(test)/%.c, $(c_re_tests))
c_rec_builds := $(patsubst $(src)/%.re_c.c, $(build)/%.c, $(c_rec_srcs))
c_rec_test_builds := $(patsubst $(test)/%.re_c.c, $(build)/%.c, $(c_rec_tests))
c_y_builds := $(patsubst $(src)/%.y, $(build)/%.c, $(y_srcs))
c_gperf_builds := $(patsubst $(src)/%.gperf.c, $(build)/%.c, $(c_gperf_srcs))
# together .re/.re_c/.y/.gperf.c
c_other_objs := $(patsubst $(build)/%.c, $(build)/%.o, $(c_re_builds) \
$(c_rec_builds) $(c_re_test_builds) $(c_rec_test_builds) $(c_y_builds) $(c_gperf_builds))
test_c_objs := $(patsubst $(test)/%.c, $(build)/$(test)/%.o, $(c_tests))
html_docs := $(patsubst $(src)/%.c, $(doc)/%.html, $(c_srcs))
cdoc := cdoc
re2c := re2c
mkdir := mkdir -p
cat := cat
zip := zip
bison := bison
#lemon := lemon
gperf := gperf
target := # -mwindows
optimize := -ffast-math
warnbasic := -Wall -pedantic #-ansi # -std=c99
# Some stuff is really new.
warnclang := -Wextra \
-Weverything \
-Wno-comma \
-Wno-logical-op-parentheses \
-Wno-parentheses \
-Wno-documentation-unknown-command \
-Wno-documentation \
-Wno-shift-op-parentheses \
-Wno-empty-body \
-Wno-padded \
-Wno-switch-enum \
-Wno-missing-noreturn \
-Wno-implicit-fallthrough
# https://stackoverflow.com/a/12099167
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
warnclang += -Wno-poison-system-directories
endif
warn := $(warnbasic) $(warnclang)
CC := clang # gcc
CF := $(target) $(optimize) $(warn)
OF := # -lm -framework OpenGL -framework GLUT or -lglut -lGLEW
# Jakob Borg and Eldar Abusalimov
# $(ARGS) is all the extra arguments; $(BRGS) is_all_the_extra_arguments
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
ifeq (backup, $(firstword $(MAKECMDGOALS)))
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
BRGS := $(subst $(SPACE),_,$(ARGS))
ifneq (,$(BRGS))
BRGS := -$(BRGS)
endif
$(eval $(ARGS):;@:)
endif
ifeq (release, $(firstword $(MAKECMDGOALS)))
CF += -funroll-loops -Ofast -D NDEBUG # -O3
OF += -Ofast
else
CF += -g
endif
######
# compiles the programme by default
default: $(bin)/$(project)
# . . . success; executable is in $(bin)/$(project)
docs: $(html_docs)
# linking
$(bin)/$(project): $(c_objs) $(c_other_objs) $(test_c_objs)
# linking rule
@$(mkdir) $(bin)
$(CC) $(OF) -o $@ $^
# compiling
#$(lemon)/$(bin)/$(lem): $(lemon)/$(src)/lemon.c
# # compiling lemon
# @$(mkdir) $(lemon)/$(bin)
# $(CC) $(CF) -o $@ $<
$(c_objs): $(build)/%.o: $(src)/%.c $(all_h)
# c_objs rule
@$(mkdir) $(build)
$(CC) $(CF) -c -o $@ $<
$(c_other_objs): $(build)/%.o: $(build)/%.c $(all_h)
# c_other_objs rule
$(CC) $(CF) -c -o $@ $<
$(test_c_objs): $(build)/$(test)/%.o: $(test)/%.c $(all_h)
# test_c_objs rule
@$(mkdir) $(build)
@$(mkdir) $(build)/$(test)
$(CC) $(CF) -c -o $@ $<
# -8 made my file 32767 lines or longer
$(c_re_builds): $(build)/%.c: $(src)/%.re.c
# *.re.c build rule
@$(mkdir) $(build)
$(re2c) -W -T -o $@ $<
$(c_re_test_builds): $(build)/$(test)/%.c: $(test)/%.re.c
# *.re.c tests rule
@$(mkdir) $(build)
@$(mkdir) $(build)/$(test)
$(re2c) -W -T -o $@ $<
$(c_rec_builds): $(build)/%.c: $(src)/%.re_c.c
# *.re_c.c (conditions) build rule
@$(mkdir) $(build)
$(re2c) -W -T -c -o $@ $<
$(c_rec_test_builds): $(build)/$(test)/%.c: $(test)/%.re_c.c
# *.re_c.c (conditions) tests rule
@$(mkdir) $(build)
@$(mkdir) $(build)/$(test)
$(re2c) -W -T -c -o $@ $<
$(c_y_builds): $(build)/%.c: $(src)/%.y # $(lemon)/$(bin)/$(lem)
# .y rule
@$(mkdir) $(build)
$(bison) -o $@ $<
$(c_gperf_builds): $(build)/%.c: $(src)/%.gperf.c
# *.gperf.c build rule
@$(mkdir) $(build)
$(gperf) $@ --output-file $<
$(html_docs): $(doc)/%.html: $(src)/%.c $(src)/%.h
# docs rule
@$(mkdir) $(doc)
cat $^ | $(cdoc) > $@
######
# phoney targets
.PHONY: setup clean backup icon install uninstall test docs release
clean:
-rm -f $(c_objs) $(test_c_objs) $(c_other_objs) $(c_re_builds) \
$(c_rec_builds) $(html_docs)
-rm -rf $(bin)/$(test)
backup:
@$(mkdir) $(backup)
$(zip) $(backup)/$(project)-`date +%Y-%m-%dT%H%M%S`$(BRGS).zip \
readme.txt Makefile $(all_h) $(all_srcs) $(all_tests) $(all_icons)
icon: default
# . . . setting icon on a Mac.
cp $(media)/$(icon) $(bin)/$(icon)
-sips --addIcon $(bin)/$(icon)
-DeRez -only icns $(bin)/$(icon) > $(bin)/$(RSRC)
-Rez -append $(bin)/$(RSRC) -o $(bin)/$(project)
-SetFile -a C $(bin)/$(project)
setup: default icon
@$(mkdir) $(bin)/$(install)
cp $(bin)/$(project) readme.txt $(bin)/$(install)
rm -f $(bin)/$(install)-MacOSX.dmg
# or rm -f $(BDIR)/$(INST)-Win32.zip
hdiutil create $(bin)/$(install)-MacOSX.dmg -volname "$(project)" -srcfolder $(bin)/$(install)
# or zip $(BDIR)/$(INST)-Win32.zip -r $(BDIR)/$(INST)
rm -R $(bin)/$(install)
# this needs work
release: clean default
strip $(bin)/$(project)
# define NDEBUG
install: release
@$(mkdir) -p $(DESTDIR)$(PREFIX)/bin
cp $(bin)/$(project) $(DESTDIR)$(PREFIX)/bin/$(project)
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(project)
docs: $(html_docs)

View File

@ -1,430 +0,0 @@
/** @license 2016 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
@abstract Stand-alone header <src/array.h>; examples <test/test_array.c>; on a
compatible workstation, `make` creates the test suite of the examples.
@subtitle Contiguous dynamic array
![Example of array.](../doc/array.png)
<tag:<A>array> is a dynamic array that stores contiguous <typedef:<PA>type>.
Resizing may be necessary when increasing the size of the array; this incurs
amortised cost, and any pointers to this memory may become stale.
@param[ARRAY_NAME, ARRAY_TYPE]
`<A>` that satisfies `C` naming conventions when mangled and a valid tag-type,
<typedef:<PA>type>, associated therewith; required. `<PA>` is private, whose
names are prefixed in a manner to avoid collisions.
@param[ARRAY_COMPARE, ARRAY_IS_EQUAL]
Compare `<CMP>` trait contained in <src/compare.h>. Requires
`<name>[<trait>]compare` to be declared as <typedef:<PCMP>compare_fn> or
`<name>[<trait>]is_equal` to be declared as <typedef:<PCMP>bipredicate_fn>,
respectfully, (but not both.)
@param[ARRAY_TO_STRING]
To string `<STR>` trait contained in <src/to_string.h>. Requires
`<name>[<trait>]to_string` be declared as <typedef:<PSTR>to_string_fn>.
@param[ARRAY_EXPECT_TRAIT, ARRAY_TRAIT]
Named traits are obtained by including `array.h` multiple times with
`ARRAY_EXPECT_TRAIT` and then subsequently including the name in
`ARRAY_TRAIT`.
@std C89 */
#if !defined(ARRAY_NAME) || !defined(ARRAY_TYPE)
#error Name or tag type undefined.
#endif
#if defined(ARRAY_TRAIT) ^ defined(BOX_TYPE)
#error ARRAY_TRAIT name must come after ARRAY_EXPECT_TRAIT.
#endif
#if defined(ARRAY_COMPARE) && defined(ARRAY_IS_EQUAL)
#error Only one can be defined at a time.
#endif
#if defined(ARRAY_TEST) && (!defined(ARRAY_TRAIT) && !defined(ARRAY_TO_STRING) \
|| defined(ARRAY_TRAIT) && !defined(ARRAY_HAS_TO_STRING))
#error Test requires to string.
#endif
#ifndef ARRAY_H /* <!-- idempotent */
#define ARRAY_H
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#if defined(ARRAY_CAT_) || defined(ARRAY_CAT) || defined(A_) || defined(PA_)
#error Unexpected defines.
#endif
/* <Kernighan and Ritchie, 1988, p. 231>. */
#define ARRAY_CAT_(n, m) n ## _ ## m
#define ARRAY_CAT(n, m) ARRAY_CAT_(n, m)
#define A_(n) ARRAY_CAT(ARRAY_NAME, n)
#define PA_(n) ARRAY_CAT(array, A_(n))
#endif /* idempotent --> */
#if !defined(restrict) && (!defined(__STDC__) || !defined(__STDC_VERSION__) \
|| __STDC_VERSION__ < 199901L)
#define ARRAY_RESTRICT /* Undo this at the end. */
#define restrict /* Attribute only in C99+. */
#endif
#ifndef ARRAY_TRAIT /* <!-- base code */
#ifndef ARRAY_MIN_CAPACITY /* <!-- !min; */
#define ARRAY_MIN_CAPACITY 3 /* > 1 */
#endif /* !min --> */
/** A valid tag type set by `ARRAY_TYPE`. */
typedef ARRAY_TYPE PA_(type);
/** Manages the array field `data` which has `size` elements. The space is
indexed up to `capacity`, which is at least `size`. The fields should be
treated as read-only; any modification is liable to cause the array to go into
an invalid state.
![States.](../doc/states.png) */
struct A_(array) { PA_(type) *data; size_t size, capacity; };
/* !data -> !size, data -> capacity >= min && size <= capacity <= max */
/** Returns null. */
static PA_(type) *PA_(null)(void) { return 0; }
/** Is `x` not null? @implements `is_element` */
static int PA_(is_element)(const PA_(type) *const x) { return !!x; }
/* @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 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 || (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 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. Unused.)
@return There was an element. @order \O(`a.size`). @implements `remove` */
static int PA_(remove)(struct PA_(iterator) *const it) {
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;
}
/** @return Iterator at element `idx` of `a`.
@implements `iterator_at` */
static struct PA_(iterator) PA_(iterator_at)(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` */
static PA_(type) *PA_(at)(const struct A_(array) *a, const size_t idx)
{ return a->data + idx; }
/** Writes `size` to `a`. @implements `tell_size` */
static void PA_(tell_size)(struct A_(array) *a, const size_t size)
{ assert(a); a->size = size; }
/** May become invalid after a topological change to any items previous. */
struct A_(array_iterator);
struct A_(array_iterator) { struct PA_(iterator) _; };
/** Zeroed data (not all-bits-zero) is initialized.
@return An idle array. @order \Theta(1) @allow */
static struct A_(array) A_(array)(void)
{ struct A_(array) a; a.data = 0, a.capacity = a.size = 0; return a; }
/** If `a` is not null, destroys and returns it to idle. @allow */
static void A_(array_)(struct A_(array) *const a)
{ if(a) free(a->data), *a = A_(array)(); }
/** @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_iterator_at)(struct A_(array) *a,
size_t idx) { struct A_(array_iterator) it;
it._ = PA_(iterator_at)(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->_); }
/** @return `it` previous element. */
static PA_(type) *A_(array_previous)(struct A_(array_iterator) *const it)
{ return assert(it), PA_(previous)(&it->_); }
/** Ensures `min` capacity of `a`. Invalidates pointers in `a`. @param[min] If
zero, does nothing. @return Success; otherwise, `errno` will be set.
@throws[ERANGE] Tried allocating more then can fit in `size_t` or `realloc`
doesn't follow POSIX. @throws[realloc] @allow */
static int A_(array_reserve)(struct A_(array) *const a, const size_t min) {
size_t c0;
PA_(type) *data;
const size_t max_size = (size_t)~0 / sizeof *a->data;
assert(a);
if(a->data) {
assert(a->size <= a->capacity);
if(min <= a->capacity) return 1;
c0 = a->capacity < ARRAY_MIN_CAPACITY
? ARRAY_MIN_CAPACITY : a->capacity;
} else { /* Idle. */
assert(!a->size && !a->capacity);
if(!min) return 1;
c0 = ARRAY_MIN_CAPACITY;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) { /* \O(\log min), in practice, negligible. */
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** The capacity of `a` will be increased to at least `n` elements beyond the
size. Invalidates any pointers in `a`.
@return The start of the buffered space at the back of the array. If `a` is
idle and `buffer` is zero, a null pointer is returned, otherwise null
indicates an error. @throws[realloc] @allow */
static PA_(type) *A_(array_buffer)(struct A_(array) *const a, const size_t n) {
assert(a);
if(a->size > (size_t)~0 - n) { errno = ERANGE; return 0; }
return A_(array_reserve)(a, a->size + n) && a->data ? a->data + a->size : 0;
}
/** Appends `n` contiguous items on the back of `a`.
@implements `append` from `BOX_CONTIGUOUS` */
static PA_(type) *PA_(append)(struct A_(array) *const a, const size_t n) {
PA_(type) *b;
if(!(b = A_(array_buffer)(a, n))) return 0;
assert(n <= a->capacity && a->size <= a->capacity - n);
return a->size += n, b;
}
/** Adds `n` un-initialised elements at position `at` in `a`. It will
invalidate any pointers in `a` if the buffer holds too few elements.
@param[at] A number smaller than or equal to `a.size`; if `a.size`, this
function behaves as <fn:<A>array_append>.
@return A pointer to the start of the new region, where there are `n`
elements. @throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_insert)(struct A_(array) *const a,
const size_t n, const size_t at) {
/* Investigate `n` is better than `element`; all the other are element. But
also, when would I ever use this? */
const size_t old_size = a->size;
PA_(type) *const b = PA_(append)(a, n);
assert(a && at <= old_size);
if(!b) return 0;
memmove(a->data + at + n, a->data + at, sizeof *a->data * (old_size - at));
return a->data + at;
}
/** @return Adds (push back) one new element of `a`. The buffer space holds at
least one element, or it may invalidate pointers in `a`.
@order amortised \O(1) @throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_new)(struct A_(array) *const a)
{ return PA_(append)(a, 1); }
/** Shrinks the capacity `a` to the size, freeing unused memory. If the size is
zero, it will be in an idle state. Invalidates pointers in `a`.
@return Success. @throws[ERANGE, realloc] (Unlikely) `realloc` error. */
static int A_(array_shrink)(struct A_(array) *const a) {
PA_(type) *data;
size_t c;
assert(a && a->capacity >= a->size);
if(!a->data) return assert(!a->size && !a->capacity), 1;
c = a->size && a->size > ARRAY_MIN_CAPACITY ? a->size : ARRAY_MIN_CAPACITY;
if(!(data = realloc(a->data, sizeof *a->data * c)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c;
return 1;
}
/** Removes `element` from `a`. Do not attempt to remove an element that is not
in `a`. @order \O(`a.size`). @allow */
static void A_(array_remove)(struct A_(array) *const a,
PA_(type) *const element) {
const size_t n = (size_t)(element - a->data);
assert(a && element && element >= a->data && element < a->data + a->size);
memmove(element, element + 1, sizeof *element * (--a->size - n));
}
/** Removes `datum` from `a` and replaces it with the tail. Do not attempt to
remove an element that is not in `a`. @order \O(1). @allow */
static void A_(array_lazy_remove)(struct A_(array) *const a,
PA_(type) *const datum) {
size_t n = (size_t)(datum - a->data);
assert(a && datum && datum >= a->data && datum < a->data + a->size);
if(--a->size != n) memcpy(datum, a->data + a->size, sizeof *datum);
}
/** Sets `a` to be empty. That is, the size of `a` will be zero, but if it was
previously in an active non-idle state, it continues to be.
@order \Theta(1) @allow */
static void A_(array_clear)(struct A_(array) *const a)
{ assert(a), a->size = 0; }
/** @return The last element or null if `a` is empty. @order \Theta(1) @allow */
static PA_(type) *A_(array_peek)(const struct A_(array) *const a)
{ return assert(a), a->size ? a->data + a->size - 1 : 0; }
/** @return Value from the the top of `a` that is removed or null if the array
is empty. @order \Theta(1) @allow */
static PA_(type) *A_(array_pop)(struct A_(array) *const a)
{ return assert(a), a->size ? a->data + --a->size : 0; }
/** Adds `n` elements to the back of `a`. It will invalidate pointers in `a` if
`n` is greater than the buffer space.
@return A pointer to the elements. If `a` is idle and `n` is zero, a null
pointer will be returned, otherwise null indicates an error.
@throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_append)(struct A_(array) *const a, const size_t n)
{ return assert(a), PA_(append)(a, n); }
/** Indices [`i0`, `i1`) of `a` will be replaced with a copy of `b`.
@param[b] Can be null, which acts as empty, but cannot overlap with `a`.
@return Success. @throws[realloc, ERANGE] @allow */
static int A_(array_splice)(struct A_(array) *restrict const a,
const struct A_(array) *restrict const b,
const size_t i0, const size_t i1) {
const size_t a_range = i1 - i0, b_range = b ? b->size : 0;
assert(a && a != b && i0 <= i1 && i1 <= a->size);
if(a_range < b_range) { /* The output is bigger. */
const size_t diff = b_range - a_range;
if(!A_(array_buffer)(a, diff)) return 0;
memmove(a->data + i1 + diff, a->data + i1,
(a->size - i1) * sizeof *a->data);
a->size += diff;
} else if(b_range < a_range) { /* The output is smaller. */
memmove(a->data + i0 + b_range, a->data + i1,
(a->size - i1) * sizeof *a->data);
a->size -= a_range - b_range;
}
if(b) memcpy(a->data + i0, b->data, b->size * sizeof *a->data);
return 1;
}
/* Box override information. */
#define BOX_TYPE struct A_(array)
#define BOX_CONTENT PA_(type) *
#define BOX_ PA_
#define BOX_MAJOR_NAME array
#define BOX_MINOR_NAME ARRAY_NAME
#define BOX_ACCESS
#define BOX_CONTIGUOUS
#ifdef HAVE_ITERATE_H /* <!-- iterate */
#include "iterate.h" /** \include */
#endif /* iterate --> */
static void PA_(unused_base_coda)(void);
static void PA_(unused_base)(void) {
PA_(null)(); 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_iterator)(0); A_(array_iterator_at)(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);
A_(array_clear)(0); A_(array_peek)(0); A_(array_pop)(0);
A_(array_append)(0, 0); A_(array_splice)(0, 0, 0, 0);
PA_(unused_base_coda)();
}
static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
#endif /* base code --> */
#ifdef ARRAY_TRAIT /* <-- trait: Will be different on different includes. */
#define BOX_TRAIT_NAME ARRAY_TRAIT
#endif /* trait --> */
#ifdef ARRAY_TO_STRING /* <!-- to string trait */
#include "to_string.h" /** \include */
#undef ARRAY_TO_STRING
#ifndef ARRAY_TRAIT
#define ARRAY_HAS_TO_STRING
#endif
#endif /* to string trait --> */
#if defined(ARRAY_TEST) && !defined(ARRAY_TRAIT) /* <!-- test base */
#include "../test/test_array.h"
#endif /* test base --> */
#if defined(ARRAY_COMPARE) || defined(ARRAY_IS_EQUAL) /* <!-- compare trait */
#ifdef ARRAY_COMPARE /* <!-- cmp */
#define COMPARE ARRAY_COMPARE
#else /* cmp --><!-- eq */
#define COMPARE_IS_EQUAL ARRAY_IS_EQUAL
#endif /* eq --> */
#include "compare.h" /** \include */
#ifdef ARRAY_TEST /* <!-- test: this detects and outputs compare test. */
#include "../test/test_array_compare.h"
#endif /* test --> */
#undef CMP_ /* From <compare.h>. */
#undef CMPEXTERN_
#ifdef ARRAY_COMPARE
#undef ARRAY_COMPARE
#else
#undef ARRAY_IS_EQUAL
#endif
#endif /* compare trait --> */
#ifdef ARRAY_EXPECT_TRAIT /* <!-- more */
#undef ARRAY_EXPECT_TRAIT
#else /* more --><!-- done */
#undef BOX_TYPE
#undef BOX_CONTENT
#undef BOX_
#undef BOX_MAJOR_NAME
#undef BOX_MINOR_NAME
#undef BOX_ACCESS
#undef BOX_CONTIGUOUS
#undef ARRAY_NAME
#undef ARRAY_TYPE
#undef ARRAY_MIN_CAPACITY
#ifdef ARRAY_HAS_TO_STRING
#undef ARRAY_HAS_TO_STRING
#endif
#ifdef ARRAY_TEST
#undef ARRAY_TEST
#endif
#endif /* done --> */
#ifdef ARRAY_TRAIT
#undef ARRAY_TRAIT
#undef BOX_TRAIT_NAME
#endif
#ifdef ARRAY_RESTRICT
#undef ARRAY_RESTRICT
#undef restrict
#endif

View File

@ -1,369 +0,0 @@
/** @license 2022 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
Is intended to use
<https://github.com/scrollmapper/bible_databases/master/txt/KJV/>.
@std C13 */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <dirent.h> /* opendir readdir closedir */
#include <unistd.h> /* chdir (POSIX) (because I'm lazy) */
/* Dynamic contiguous string that is used to load files. */
#define ARRAY_NAME char
#define ARRAY_TYPE char
#include "../src/array.h"
/** Append a text file, `fn`, to `c`, and add a '\0'.
@return The start of the appended file or null on error. A partial read is a
failure. @throws[fopen, fread, malloc]
@throws[EISEQ] The text file has embedded nulls.
@throws[ERANGE] If the standard library does not follow POSIX. */
static char *append_file(struct char_array *text, const char *const fn) {
FILE *fp = 0;
const size_t granularity = 1024;
size_t nread, start;
char *cursor;
int success = 1;
assert(text && fn);
start = text->size;
if(!(fp = fopen(fn, "r"))) goto catch;
/* Read entire file in chunks. */
do if(!(cursor = char_array_buffer(text, granularity))
|| (nread = fread(cursor, 1, granularity, fp), ferror(fp))
|| !char_array_append(text, nread)) goto catch;
while(nread == granularity);
/* File to `C` string. */
if(!(cursor = char_array_new(text))) goto catch;
*cursor = '\0';
/* Binary files with embedded '\0' are not allowed; check just this read. */
if(strchr(text->data + start, '\0') != cursor)
{ errno = EILSEQ; goto catch; }
goto finally;
catch:
if(!errno) errno = EILSEQ; /* Will never be true on POSIX. */
success = 0;
finally:
if(fp) fclose(fp);
return success ? text->data + start : 0;
}
/** Helper to parse unsigned; [`s`,`e`) => `n`. */
static int parse_natural(const char *s, const char *const e, unsigned *const n) {
unsigned accum = 0;
while(s < e) {
unsigned next = accum * 10 + (unsigned)(*s - '0');
if(accum >= next) return errno = ERANGE, 0;
accum = next;
s++;
}
*n = accum;
return 1;
}
/* Enumerate books. */
#define BOOKS \
X(Genesis),\
X(Exodus),\
X(Leviticus),\
X(Numbers),\
X(Deuteronomy),\
X(Joshua),\
X(Judges),\
X(Ruth),\
X(ISamuel),\
X(IISamuel),\
X(IKings),\
X(IIKings),\
X(IChronicles),\
X(IIChronicles),\
X(Ezra),\
X(Nehemiah),\
X(Esther),\
X(Job),\
X(Psalms),\
X(Proverbs),\
X(Ecclesiastes),\
X(Song_of_Solomon),\
X(Isaiah),\
X(Jeremiah),\
X(Lamentations),\
X(Ezekiel),\
X(Daniel),\
X(Hosea),\
X(Joel),\
X(Amos),\
X(Obadiah),\
X(Jonah),\
X(Micah),\
X(Nahum),\
X(Habakkuk),\
X(Zephaniah),\
X(Haggai),\
X(Zechariah),\
X(Malachi),\
\
X(Matthew),\
X(Mark),\
X(Luke),\
X(John),\
X(Acts),\
X(Romans),\
X(ICorinthians),\
X(IICorinthians),\
X(Galatians),\
X(Ephesians),\
X(Philippians),\
X(Colossians),\
X(IThessalonians),\
X(IIThessalonians),\
X(ITimothy),\
X(IITimothy),\
X(Titus),\
X(Philemon),\
X(Hebrews),\
X(James),\
X(IPeter),\
X(IIPeter),\
X(IJohn),\
X(IIJohn),\
X(IIIJohn),\
X(Jude),\
X(Revelation),\
X(KJV_BOOK_SIZE)
#define X(book) book
enum kjv_book { BOOKS };
#undef X
#define X(book) #book
static const char *kjv_book_string[] = { BOOKS };
#undef X
#undef BOOKS
/* Parse filename of books. This works with
<https://github.com/scrollmapper/bible_databases/tree/master/txt/KJV> */
/*!re2c /**/
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = char;
natural = [1-9][0-9]*;
whitespace = [ \t\v\f];
word = [^ \t\v\f\n\x00]+;
*/
/** `fn` contains "<number>[*].txt", sticks that in `book_no`, otherwise
returns false. */
static int kjv_filename(const char *fn, unsigned *const book_no) {
const char *YYCURSOR = fn, *YYMARKER, *yyt1, *yyt2, *s0, *s1;
assert(fn && book_no);
/*!re2c /**/
*
{ return 0; }
@s0 natural @s1 [^.\x00]* ".txt" "\x00"
{ return parse_natural(s0, s1, book_no); }
*/
}
/* Parse book contents. */
struct lex {
size_t line;
const char *cursor;
int error;
unsigned chapter, verse, words;
};
static struct lex lex(const char *cursor) {
struct lex lex;
assert(cursor);
lex.line = 1;
lex.cursor = cursor;
lex.error = 0;
lex.chapter = lex.verse = lex.words = 0;
return lex;
}
/*!conditions:re2c*/
static int lex_next_verse(struct lex *const lex) {
const char *YYMARKER, *yyt1 = 0, *yyt2 = 0, *s0, *s1, *t0, *t1;
enum YYCONDTYPE condition = yycline;
/*!re2c /**/
re2c:define:YYCURSOR = lex->cursor;
re2c:define:YYGETCONDITION = "condition";
re2c:define:YYSETCONDITION = "condition = @@;";
re2c:define:YYGETCONDITION:naked = 1;
re2c:define:YYSETCONDITION:naked = 1; */
assert(lex && lex->cursor);
lex->error = 0;
scan:
/*!re2c /**/
<*> * { return errno = EILSEQ, lex->error = 1, 0; }
<line> [^[\]\n\x00]* "\n" { lex->line++; goto scan; }
<line> "\x00" { return 0; }
<line> "[" @s0 natural @s1 ":" @t0 natural @t1 "]" => verse {
if(!parse_natural(s0, s1, &lex->chapter)
|| !parse_natural(t0, t1, &lex->verse))
return errno = EILSEQ, lex->error = 1, 0;
lex->words = 0;
/*printf("%u:%u", lex->chapter, lex->verse);*/
goto scan;
}
<verse> whitespace+ { goto scan; }
<verse> @s0 word @s1 { lex->words++; goto scan; }
<verse> "\n" { /*printf(" -> %u\n", lex->words);*/ lex->line++; return 1; }
*/
}
/* Reversible hash map to store data on bible. */
#include <stdint.h>
/** <https://nullprogram.com/blog/2018/07/31/>
<https://github.com/skeeto/hash-prospector> on `x`. */
static uint32_t lowbias32(uint32_t x) {
x ^= x >> 16;
x *= 0x7feb352dU;
x ^= x >> 15;
x *= 0x846ca68bU;
x ^= x >> 16;
return x;
}
/* Inverts `x`. */
static uint32_t lowbias32_r(uint32_t x) {
x ^= x >> 16;
x *= 0x43021123U;
x ^= x >> 15 ^ x >> 30;
x *= 0x1d69e2a5U;
x ^= x >> 16;
return x;
}
/** Two hash-tables use the same structure. */
union kjvcite {
/* Overkill, but no initializing unused bits, 12 + 13 + 7 = 32. */
struct { unsigned verse : 12, chapter : 13, book : 7; };
uint32_t u32;
};
static uint32_t kjv_hash(const union kjvcite x) { return lowbias32(x.u32); }
static union kjvcite kjv_unhash(const uint32_t x) {
union kjvcite k;
k.u32 = lowbias32_r(x);
return k;
}
static void kjv_to_string(const union kjvcite x, char (*const a)[12])
{ sprintf(*a, "%.4s%u:%u", kjv_book_string[x.book],
(x.chapter + 1) % 1000, (x.verse + 1) % 1000); }
/** Derived information on verse word count. */
static uint32_t verse_hash(const union kjvcite x) { return kjv_hash(x); }
static union kjvcite verse_unhash(const uint32_t x) { return kjv_unhash(x); }
static void verse_to_string(const union kjvcite x, char (*const a)[12])
{ kjv_to_string(x, a); }
#define TABLE_NAME verse
#define TABLE_KEY union kjvcite
#define TABLE_UINT uint32_t
#define TABLE_VALUE unsigned
#define TABLE_INVERSE
#define TABLE_DEFAULT 0
#define TABLE_TO_STRING
#include "../src/table.h"
/* A set of verses. */
static uint32_t kjvset_hash(const union kjvcite x) { return kjv_hash(x); }
static union kjvcite kjvset_unhash(const uint32_t x) { return kjv_unhash(x); }
static void kjvset_to_string(const union kjvcite x, char (*const a)[12])
{ kjv_to_string(x, a); }
#define TABLE_NAME kjvset
#define TABLE_KEY union kjvcite
#define TABLE_UINT uint32_t
#define TABLE_INVERSE
#define TABLE_TO_STRING
#include "../src/table.h"
int main(void) {
const char *const dir_kjv = "KJV";
struct {
struct char_array backing;
struct verse_table verses;
size_t words;
} kjv = { 0 };
DIR *dir = 0;
struct dirent *de = 0;
struct { size_t offset; int is; } build[KJV_BOOK_SIZE] = { 0 };
enum kjv_book b = 0;
int success = EXIT_SUCCESS, attempted_closedir = 0;
errno = 0;
/* For all files in directory KJV with <#>*.txt, read into backing. */
if(chdir(dir_kjv) == -1 || !(dir = opendir("."))) goto catch;
while((de = readdir(dir))) {
unsigned ordinal;
char *unstable_book;
if(!kjv_filename(de->d_name, &ordinal)) continue; /* Extract no. */
/*fprintf(stderr, "<%s> ordinal: %u\n", de->d_name, ordinal);*/
if(ordinal < 1 || ordinal > KJV_BOOK_SIZE)
{ errno = ERANGE; goto catch; } /* Not in range. */
if(build[b = ordinal - 1].is) /* Convert to zero-based. */
{ errno = EDOM; goto catch; } /* Is duplicate. */
if(!(unstable_book = append_file(&kjv.backing, de->d_name))) goto catch;
build[b].is = 1;
build[b].offset = (size_t)(unstable_book - kjv.backing.data);
}
if(attempted_closedir = 1, closedir(dir) == -1) goto catch; dir = 0;
/* Now backing is stable; count all the words for each verse. */
for(b = 0; b < KJV_BOOK_SIZE; b++) {
struct lex x;
if(!build[b].is) { fprintf(stderr, "Missing book [%u]%s.\n",
b + 1, kjv_book_string[b]); errno = EDOM; goto catch; }
x = lex(kjv.backing.data + build[b].offset);
while(lex_next_verse(&x)) {
const union kjvcite cite
= { .book = b, .chapter = x.chapter, .verse = x.verse };
unsigned *words;
switch(verse_table_assign(&kjv.verses, cite, &words)) {
case TABLE_PRESENT: fprintf(stderr, "[%u]%s %u:%u duplicated.\n",
b + 1, kjv_book_string[b], x.chapter, x.verse); errno = EDOM;
case TABLE_ERROR: goto catch;
case TABLE_ABSENT: break;
}
*words = x.words, kjv.words += x.words;
}
if(x.error) { fprintf(stderr, "[%u]%s on line %zu\n",
b + 1, kjv_book_string[b], x.line); goto catch; }
}
printf("words: %s\n", verse_table_to_string(&kjv.verses));
printf("kjv: %zu total words\n", kjv.words);
{
union kjvcite c;
struct verse_table_iterator it = verse_table_begin(&kjv.verses);
unsigned *w;
while(verse_table_next(&it, &c, &w))
printf("%s %u:%u -> %u\n",
kjv_book_string[c.book], c.chapter, c.verse, *w);
c = (union kjvcite){ .book = Genesis, .chapter = 1, .verse = 1 };
printf("1:1:1 -> %u\n", verse_table_get(&kjv.verses, c));
}
goto finally;
catch:
success = EXIT_FAILURE;
if(de) fprintf(stderr, "While reading %s.\n", de->d_name);
perror(de ? de->d_name : dir_kjv);
if(dir && !attempted_closedir && closedir(dir) == -1) perror(dir_kjv);
finally:
verse_table_(&kjv.verses);
char_array_(&kjv.backing);
return success;
}

View File

@ -1,934 +0,0 @@
/** @license 2019 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
@abstract Stand-alone header <src/table.h>; examples <test/test_table.c>;
article <doc/table.pdf>. On a compatible workstation, `make` creates the
test suite of the examples.
@subtitle Hash table
![Example of <string>table.](../doc/table.png)
<tag:<N>table> implements a set or map of <typedef:<PN>entry> as a hash table.
It must be supplied <typedef:<PN>hash_fn> `<N>hash` and,
<typedef:<PN>is_equal_fn> `<N>is_equal` or <typedef:<PN>unhash_fn> `<N>unhash`.
(Fixme: remove entry as public struct, this should be entirely private.)
@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_INVERSE]
By default it assumes that `<N>is_equal` is supplied; with this, instead
requires `<N>unhash` satisfying <typedef:<PN>unhash_fn>.
@param[TABLE_VALUE]
An optional type that is the payload of the key, thus making this a map or
associative array.
@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_DEFAULT]
Default trait; a <typedef:<PN>value> used in <fn:<N>table<D>get>.
@param[TABLE_TO_STRING]
To string trait `<STR>` contained in <src/to_string.h>. Require
`<name>[<trait>]to_string` be declared as <typedef:<PSTR>to_string_fn>.
@param[TABLE_EXPECT_TRAIT, TABLE_TRAIT]
Named traits are obtained by including `table.h` multiple times with
`TABLE_EXPECT_TRAIT` and then subsequently including the name in
`TABLE_TRAIT`.
@std C89 */
#if !defined(TABLE_NAME) || !defined(TABLE_KEY)
#error Name TABLE_NAME or tag type TABLE_KEY undefined.
#endif
#if defined(TABLE_TRAIT) ^ defined(BOX_TYPE)
#error TABLE_TRAIT name must come after TABLE_EXPECT_TRAIT.
#endif
#if defined(TABLE_TEST) && (!defined(TABLE_TRAIT) && !defined(TABLE_TO_STRING) \
|| defined(TABLE_TRAIT) && !defined(TABLE_HAS_TO_STRING))
#error Test requires to string.
#endif
#ifndef TABLE_H /* <!-- idempotent */
#define TABLE_H
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#if defined(TABLE_CAT_) || defined(TABLE_CAT) || defined(N_) || defined(PN_)
#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))
/* 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 may save power? We cannot save this in an
`enum` because we don't know maximum. */
#define TABLE_M1 ((PN_(uint))~(PN_(uint))0) /* 2's compliment -1. */
#define TABLE_HIGH ((TABLE_M1 >> 1) + 1) /* Cardinality must be 1111... */
#define TABLE_END (TABLE_HIGH)
#define TABLE_NULL (TABLE_HIGH + 1)
#define TABLE_RESULT X(ERROR), X(ABSENT), X(PRESENT)
#define X(n) TABLE_##n
/** A result of modifying the table, of which `TABLE_ERROR` is false.
![A diagram of the result states.](../doc/put.png) */
enum table_result { TABLE_RESULT };
#undef X
#define X(n) #n
/** A static array of strings describing the <tag:table_result>. */
static const char *const table_result_str[] = { TABLE_RESULT };
#undef X
#undef TABLE_RESULT
#endif /* idempotent --> */
#ifndef TABLE_TRAIT /* <!-- base code */
#ifndef TABLE_UINT
#define TABLE_UINT size_t
#endif
/** <typedef:<PN>hash_fn> returns this hash type by `TABLE_UINT`, which must be
be an unsigned integer. Places a simplifying limit on the maximum number of
elements of half the cardinality. */
typedef TABLE_UINT PN_(uint);
/** Valid tag type defined by `TABLE_KEY` used for keys. If `TABLE_INVERSE` is
not defined, a copy of this value will be stored in the internal buckets. */
typedef TABLE_KEY PN_(key);
typedef const TABLE_KEY PN_(key_c); /* Works 90%? */
#if 0 /* <!-- documentation */
/** A map from <typedef:<PN>key_c> onto <typedef:<PN>uint>, called `<N>hash`,
that, ideally, should be easy to compute while minimizing duplicate addresses.
Must be consistent for each value while in the table. If <typedef:<PN>key> is
a pointer, one is permitted to have null in the domain. */
typedef PN_(uint) (*PN_(hash_fn))(const PN_(key));
#ifdef TABLE_INVERSE /* <!-- inv */
/** Defining `TABLE_INVERSE` says <typedef:<PN>hash_fn> forms a bijection
between the range in <typedef:<PN>key> and the image in <typedef:<PN>uint>,
and the inverse is called `<N>unhash`. In this case, keys are not stored
in the hash table, rather they are generated using this inverse-mapping. */
typedef PN_(key) (*PN_(unhash_fn))(PN_(uint));
#else /* inv --><!-- !inv */
/** Equivalence relation between <typedef:<PN>key> that satisfies
`<PN>is_equal_fn(a, b) -> <PN>hash(a) == <PN>hash(b)`, called `<N>is_equal`.
If `TABLE_INVERSE` is set, there is no need for this function because the
comparison is done directly in hash space. */
typedef int (*PN_(is_equal_fn))(PN_(key_c) a, PN_(key_c) b);
#endif /* !inv --> */
#endif /* documentation --> */
#ifdef TABLE_VALUE /* <!-- value */
/** Defining `TABLE_VALUE` produces an associative map, otherwise it is the
same as <typedef:<PN>key>. */
typedef TABLE_VALUE PN_(value);
/** Defining `TABLE_VALUE` creates this map from <typedef:<PN>key> to
<typedef:<PN>value>, as an interface with table. */
struct N_(table_entry) { PN_(key) key; PN_(value) value; };
/** If `TABLE_VALUE`, this is <tag:<N>table_entry>; otherwise, it's the same as
<typedef:<PN>key>. */
typedef struct N_(table_entry) PN_(entry);
#else /* value --><!-- !value */
typedef PN_(key) PN_(value);
typedef PN_(key) PN_(entry);
#endif /* !value --> */
/** @return Key from `e`. */
static PN_(key) PN_(entry_key)(PN_(entry) e) {
#ifdef TABLE_VALUE
return e.key;
#else
return e;
#endif
}
/* Address is hash modulo size of table. Any occupied buckets at the head of
the linked structure are closed, that is, the address equals the index. These
form a linked table, possibly with other, open buckets that have the same
address in vacant buckets. */
struct PN_(bucket) {
PN_(uint) next; /* Bucket index, including `TABLE_NULL` and `TABLE_END`. */
PN_(uint) hash;
#ifndef TABLE_INVERSE
PN_(key) key;
#endif
#ifdef TABLE_VALUE
PN_(value) value;
#endif
};
/** Gets the key of an occupied `bucket`. */
static PN_(key) PN_(bucket_key)(const struct PN_(bucket) *const bucket) {
assert(bucket && bucket->next != TABLE_NULL);
#ifdef TABLE_INVERSE
/* On `TABLE_INVERSE`, this function must be defined by the user. */
return N_(unhash)(bucket->hash);
#else
return bucket->key;
#endif
}
/** Gets the value of an occupied `bucket`, which might be the same as the
key. */
static PN_(value) PN_(bucket_value)(const struct PN_(bucket) *const bucket) {
assert(bucket && bucket->next != TABLE_NULL);
#ifdef TABLE_VALUE
return bucket->value;
#else
return PN_(bucket_key)(bucket);
#endif
}
/** Fills `entry` with the information of `bucket`. */
static PN_(entry) PN_(to_entry)(struct PN_(bucket) *const bucket) {
PN_(entry) e;
assert(bucket);
#ifdef TABLE_VALUE /* entry { <PN>key key; <PN>value value; } */
e.key = PN_(bucket_key)(bucket);
e.value = bucket->value;
#else /* entry <PN>key */
e = PN_(bucket_key)(bucket);
#endif
return e;
}
/** Returns true if the `replace` replaces the `original`.
(Shouldn't it be entry?) */
typedef int (*PN_(policy_fn))(PN_(key) original, PN_(key) replace);
/** To initialize, see <fn:<N>table>, `TABLE_IDLE`, `{0}` (`C99`,) or being
`static`. The fields should be treated as read-only; any modification is
liable to cause the table to go into an invalid state.
![States.](../doc/states.png) */
struct N_(table) { /* "Padding size," good. */
struct PN_(bucket) *buckets; /* @ has zero/one key specified by `next`. */
/* `size <= capacity`; size is not needed but convenient and allows
short-circuiting. Top is an index of the stack, potentially lazy: MSB
stores whether this is a step ahead (which would make it less, the stack
grows from the bottom,) otherwise it is right at the top, */
PN_(uint) log_capacity, size, top;
};
/** The capacity of a non-idle `table` is always a power-of-two. */
static PN_(uint) PN_(capacity)(const struct N_(table) *const table)
{ return assert(table && table->buckets && table->log_capacity >= 3),
(PN_(uint))((PN_(uint))1 << table->log_capacity); }
/** @return Indexes the first closed bucket in the set of buckets with the same
address from non-idle `table` given the `hash`. If the bucket is empty, it
will have `next = TABLE_NULL` or it's own <fn:<PN>to_bucket_no> not equal to
the index. */
static PN_(uint) PN_(to_bucket_no)(const struct N_(table) *const table,
const PN_(uint) hash) { return hash & (PN_(capacity)(table) - 1); }
/** @return Search for the previous link in the bucket to `b` in `table`, if it
exists, (by restarting and going though the list.) This is not the same as the
iterator. @order \O(`bucket size`) */
static struct PN_(bucket) *PN_(prev)(const struct N_(table) *const table,
const PN_(uint) b) {
const struct PN_(bucket) *const bucket = table->buckets + b;
PN_(uint) to_next = TABLE_NULL, next;
assert(table && bucket->next != TABLE_NULL);
/* Note that this does not check for corrupted tables; would get assert. */
for(next = PN_(to_bucket_no)(table, bucket->hash);
/* assert(next < capacity), */ next != b;
to_next = next, next = table->buckets[next].next);
return to_next != TABLE_NULL ? table->buckets + to_next : 0;
}
/* <!-- stack functions */
/** On return, the `top` of `table` will be empty and eager, but size is not
incremented, leaving it in intermediate state. Amortized if you grow only. */
static void PN_(grow_stack)(struct N_(table) *const table) {
/* Subtract one for eager. */
PN_(uint) top = (table->top & ~TABLE_HIGH) - !(table->top & TABLE_HIGH);
assert(table && table->buckets && table->top && top < PN_(capacity)(table));
while(table->buckets[top].next != TABLE_NULL) assert(top), top--;
table->top = top; /* Eager, since one is allegedly going to fill it. */
}
/** Force the evaluation of the stack of `table`, thereby making it eager. This
is like searching for a bucket in open-addressing. @order \O(`buckets`) */
static void PN_(force_stack)(struct N_(table) *const table) {
PN_(uint) top = table->top;
if(top & TABLE_HIGH) { /* Lazy. */
struct PN_(bucket) *bucket;
top &= ~TABLE_HIGH;
do bucket = table->buckets + ++top/*, assert(top < capacity)*/;
while(bucket->next != TABLE_NULL
&& PN_(to_bucket_no)(table, bucket->hash) == top);
table->top = top; /* Eager. */
}
}
/** Is `i` in `table` possibly on the stack? (The stack grows from the high.) */
static int PN_(in_stack_range)(const struct N_(table) *const table,
const PN_(uint) i) {
return assert(table && table->buckets),
(table->top & ~TABLE_HIGH) + !!(table->top & TABLE_HIGH) <= i;
}
/** Corrects newly-deleted `b` from `table` in the stack. */
static void PN_(shrink_stack)(struct N_(table) *const table,
const PN_(uint) b) {
assert(table && table->buckets && b < PN_(capacity)(table));
assert(table->buckets[b].next == TABLE_NULL);
if(!PN_(in_stack_range)(table, b)) return;
PN_(force_stack)(table); /* Only have room for 1 step of laziness. */
assert(PN_(in_stack_range)(table, b)); /* I think this is assured? Think. */
if(b != table->top) {
struct PN_(bucket) *const prev = PN_(prev)(table, table->top);
table->buckets[b] = table->buckets[table->top];
prev->next = b;
}
table->buckets[table->top].next = TABLE_NULL;
table->top |= TABLE_HIGH; /* Lazy. */
}
/** Moves the `m` index in non-idle `table`, to the top of collision stack.
This may result in an inconsistent state; one is responsible for filling that
hole and linking it with top. */
static void PN_(move_to_top)(struct N_(table) *const table, const PN_(uint) m) {
struct PN_(bucket) *move, *top, *prev;
assert(table
&& table->size < PN_(capacity)(table) && m < PN_(capacity)(table));
PN_(grow_stack)(table); /* Leaves it in an eager state. */
move = table->buckets + m, top = table->buckets + table->top;
assert(move->next != TABLE_NULL && top->next == TABLE_NULL);
if(prev = PN_(prev)(table, m)) prev->next = table->top; /* \O(|`bucket`|) */
memcpy(top, move, sizeof *move), move->next = TABLE_NULL;
}
/* stack --> */
/** `TABLE_INVERSE` is injective, so in that case, we only compare hashes.
@return `a` and `b`. */
static int PN_(equal_buckets)(PN_(key_c) a, PN_(key_c) b) {
#ifdef TABLE_INVERSE
return (void)a, (void)b, 1;
#else
/* Must have this function declared. */
return N_(is_equal)(a, b);
#endif
}
/** `table` will be searched linearly for `key` which has `hash`. */
static struct PN_(bucket) *PN_(query)(struct N_(table) *const table,
PN_(key_c) key, const PN_(uint) hash) {
struct PN_(bucket) *bucket1;
PN_(uint) head, b0 = TABLE_NULL, b1, b2;
assert(table && table->buckets && table->log_capacity);
bucket1 = table->buckets + (head = b1 = PN_(to_bucket_no)(table, hash));
/* Not the start of a bucket: empty or in the collision stack. */
if((b2 = bucket1->next) == TABLE_NULL
|| PN_(in_stack_range)(table, b1)
&& b1 != PN_(to_bucket_no)(table, bucket1->hash)) return 0;
while(hash != bucket1->hash
|| !PN_(equal_buckets)(key, PN_(bucket_key)(bucket1))) {
if(b2 == TABLE_END) return 0;
bucket1 = table->buckets + (b0 = b1, b1 = b2);
assert(b1 < PN_(capacity)(table) && PN_(in_stack_range)(table, b1)
&& b1 != TABLE_NULL);
b2 = bucket1->next;
}
#ifdef TABLE_DONT_SPLAY /* <!-- !splay: (No reason not to, practically.) */
return bucket1;
#undef TABLE_DONT_SPLAY
#else /* !splay --><!-- splay: bring the MRU to the front. */
if(b0 == TABLE_NULL) return bucket1;
{
struct PN_(bucket) *const bucket0 = table->buckets + b0,
*const bucket_head = table->buckets + head, temp;
bucket0->next = b2;
memcpy(&temp, bucket_head, sizeof *bucket_head);
memcpy(bucket_head, bucket1, sizeof *bucket1);
memcpy(bucket1, &temp, sizeof temp);
bucket_head->next = b1;