updating boxes for head/body new; better
This commit is contained in:
parent
a52d8ce0ba
commit
2844eeffd5
31
src/array.h
31
src/array.h
@ -31,6 +31,13 @@
|
|||||||
`ARRAY_EXPECT_TRAIT` and then subsequently including the name in
|
`ARRAY_EXPECT_TRAIT` and then subsequently including the name in
|
||||||
`ARRAY_TRAIT`.
|
`ARRAY_TRAIT`.
|
||||||
|
|
||||||
|
@param[ARRAY_HEAD, ARRAY_BODY]
|
||||||
|
These go together to allow exporting non-static data between compilation units
|
||||||
|
by separating the `ARRAY_HEAD`, which is intended to go in the header, with
|
||||||
|
`ARRAY_NAME` and `ARRAY_TYPE`, and `ARRAY_BODY` functions. [All static
|
||||||
|
functions will have `s_` prepended for ease of creating a public thunk
|
||||||
|
functions, which most likely will conflict with static functions.]
|
||||||
|
|
||||||
@std C89 */
|
@std C89 */
|
||||||
|
|
||||||
#if !defined(ARRAY_NAME) || !defined(ARRAY_TYPE)
|
#if !defined(ARRAY_NAME) || !defined(ARRAY_TYPE)
|
||||||
@ -46,6 +53,9 @@
|
|||||||
|| defined(ARRAY_TRAIT) && !defined(ARRAY_HAS_TO_STRING))
|
|| defined(ARRAY_TRAIT) && !defined(ARRAY_HAS_TO_STRING))
|
||||||
#error Test requires to string.
|
#error Test requires to string.
|
||||||
#endif
|
#endif
|
||||||
|
#if defined ARRAY_HEAD && defined ARRAY_BODY
|
||||||
|
#error Can not be ARRAY_HEAD and ARRAY_BODY.
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef ARRAY_H /* <!-- idempotent */
|
#ifndef ARRAY_H /* <!-- idempotent */
|
||||||
#define ARRAY_H
|
#define ARRAY_H
|
||||||
@ -76,6 +86,8 @@
|
|||||||
#define ARRAY_MIN_CAPACITY 3 /* > 1 */
|
#define ARRAY_MIN_CAPACITY 3 /* > 1 */
|
||||||
#endif /* !min --> */
|
#endif /* !min --> */
|
||||||
|
|
||||||
|
#ifndef ARRAY_BODY /* <!-- head */
|
||||||
|
|
||||||
/** A valid tag type set by `ARRAY_TYPE`. */
|
/** A valid tag type set by `ARRAY_TYPE`. */
|
||||||
typedef ARRAY_TYPE PA_(type);
|
typedef ARRAY_TYPE PA_(type);
|
||||||
|
|
||||||
@ -88,6 +100,13 @@ struct A_(array) { PA_(type) *data; size_t size, capacity; };
|
|||||||
|
|
||||||
/* `a` non-null; `i >= elements` empty; insert-delete on left like C++. */
|
/* `a` non-null; `i >= elements` empty; insert-delete on left like C++. */
|
||||||
struct PA_(iterator) { struct A_(array) *a; size_t i; };
|
struct PA_(iterator) { struct A_(array) *a; size_t i; };
|
||||||
|
/** May become invalid after a topological change to any items previous. */
|
||||||
|
struct A_(array_iterator);
|
||||||
|
struct A_(array_iterator) { struct PA_(iterator) _; };
|
||||||
|
|
||||||
|
#endif /* head --> */
|
||||||
|
#ifndef ARRAY_HEAD /* <!-- body */
|
||||||
|
|
||||||
/** @return Iterator at end of (non-null) valid `a`. */
|
/** @return Iterator at end of (non-null) valid `a`. */
|
||||||
static struct PA_(iterator) PA_(iterator)(struct A_(array) *const a) {
|
static struct PA_(iterator) PA_(iterator)(struct A_(array) *const a) {
|
||||||
struct PA_(iterator) it;
|
struct PA_(iterator) it;
|
||||||
@ -126,10 +145,6 @@ static PA_(type) *PA_(at)(const struct A_(array) *a, const size_t idx)
|
|||||||
static void PA_(tell_size)(struct A_(array) *a, const size_t size)
|
static void PA_(tell_size)(struct A_(array) *a, const size_t size)
|
||||||
{ assert(a); a->size = 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.
|
/** Zeroed data (not all-bits-zero) is initialized.
|
||||||
@return An idle array. @order \Theta(1) @allow */
|
@return An idle array. @order \Theta(1) @allow */
|
||||||
static struct A_(array) A_(array)(void)
|
static struct A_(array) A_(array)(void)
|
||||||
@ -321,6 +336,8 @@ static void PA_(unused_base)(void) {
|
|||||||
}
|
}
|
||||||
static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
|
static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
|
||||||
|
|
||||||
|
#endif /* body --> */
|
||||||
|
|
||||||
#endif /* base code --> */
|
#endif /* base code --> */
|
||||||
|
|
||||||
|
|
||||||
@ -392,6 +409,12 @@ static void PAT_(to_string)(const PA_(type) *e, char (*const a)[12])
|
|||||||
#ifdef ARRAY_TEST
|
#ifdef ARRAY_TEST
|
||||||
#undef ARRAY_TEST
|
#undef ARRAY_TEST
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ARRAY_BODY
|
||||||
|
#undef ARRAY_BODY
|
||||||
|
#endif
|
||||||
|
#ifdef ARRAY_HEAD
|
||||||
|
#undef ARRAY_HEAD
|
||||||
|
#endif
|
||||||
#endif /* done --> */
|
#endif /* done --> */
|
||||||
#ifdef ARRAY_TRAIT
|
#ifdef ARRAY_TRAIT
|
||||||
#undef ARRAY_TRAIT
|
#undef ARRAY_TRAIT
|
||||||
|
@ -31,7 +31,7 @@ struct day_tree_iterator { struct tree_day_iterator _; };
|
|||||||
#if defined PROTO \
|
#if defined PROTO \
|
||||||
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
struct journal { struct day_tree days; struct text backing; };
|
struct journal { struct day_tree days; struct char_array backing; };
|
||||||
struct journal_iterator { struct day_tree_iterator _; };
|
struct journal_iterator { struct day_tree_iterator _; };
|
||||||
struct journal journal(void);
|
struct journal journal(void);
|
||||||
void journal_(struct journal *);
|
void journal_(struct journal *);
|
||||||
|
@ -198,7 +198,7 @@ struct journal journal(void) {
|
|||||||
}
|
}
|
||||||
/* Because it's in a flat array, the pointers are not stable
|
/* Because it's in a flat array, the pointers are not stable
|
||||||
while we are loading it, and we need to store the offsets. */
|
while we are loading it, and we need to store the offsets. */
|
||||||
*v.offset = (uintptr_t)(contents - j.backing.a.data);
|
*v.offset = (uintptr_t)(contents - j.backing.data);
|
||||||
}
|
}
|
||||||
d = 0, int_array_clear(&days);
|
d = 0, int_array_clear(&days);
|
||||||
if(chdir("..") == -1) goto catch;
|
if(chdir("..") == -1) goto catch;
|
||||||
@ -214,7 +214,7 @@ struct journal journal(void) {
|
|||||||
while(day_tree_next(&it)) {
|
while(day_tree_next(&it)) {
|
||||||
v.text = day_tree_value(&it);
|
v.text = day_tree_value(&it);
|
||||||
/*printf("[%zu]...", *v.offset);*/
|
/*printf("[%zu]...", *v.offset);*/
|
||||||
*v.text = j.backing.a.data + *v.offset;
|
*v.text = j.backing.data + *v.offset;
|
||||||
/*printf("<%.32s>\n", *v.text);*/
|
/*printf("<%.32s>\n", *v.text);*/
|
||||||
}
|
}
|
||||||
/*fprintf(stderr, "Journal has entries: %s\n",
|
/*fprintf(stderr, "Journal has entries: %s\n",
|
||||||
@ -234,7 +234,7 @@ finally:
|
|||||||
|
|
||||||
/** @return `j` read in some data? */
|
/** @return `j` read in some data? */
|
||||||
int journal_is_empty(const struct journal *const j) {
|
int journal_is_empty(const struct journal *const j) {
|
||||||
return !j || !j->days.root.node || !j->backing.a.data;
|
return !j || !j->days.root.node || !j->backing.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return `j` as a string. */
|
/** @return `j` as a string. */
|
||||||
|
@ -160,7 +160,7 @@ void kjvcount_(struct kjvcount *const count) {
|
|||||||
<fn:kjvcount_is_empty> to tell. */
|
<fn:kjvcount_is_empty> to tell. */
|
||||||
struct kjvcount kjvcount(void) {
|
struct kjvcount kjvcount(void) {
|
||||||
const char *const dir_kjv = "kjv";
|
const char *const dir_kjv = "kjv";
|
||||||
struct text backing = text();
|
struct char_array backing = text();
|
||||||
struct kjvcount count = {0};
|
struct kjvcount count = {0};
|
||||||
DIR *dir = 0;
|
DIR *dir = 0;
|
||||||
struct dirent *de = 0;
|
struct dirent *de = 0;
|
||||||
@ -183,7 +183,7 @@ struct kjvcount kjvcount(void) {
|
|||||||
if(!(unstable_backing = text_append_file(&backing, de->d_name)))
|
if(!(unstable_backing = text_append_file(&backing, de->d_name)))
|
||||||
goto catch;
|
goto catch;
|
||||||
build[b].is = 1;
|
build[b].is = 1;
|
||||||
build[b].offset = (size_t)(unstable_backing - backing.a.data);
|
build[b].offset = (size_t)(unstable_backing - backing.data);
|
||||||
}
|
}
|
||||||
if(closedir(dir) == -1) { dir = 0; goto catch; } dir = 0;
|
if(closedir(dir) == -1) { dir = 0; goto catch; } dir = 0;
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ struct kjvcount kjvcount(void) {
|
|||||||
struct lex x;
|
struct lex x;
|
||||||
if(!build[b].is) { fprintf(stderr, "Missing book [%u]%s.\n",
|
if(!build[b].is) { fprintf(stderr, "Missing book [%u]%s.\n",
|
||||||
b + 1, kjv_book_string[b]); errno = EDOM; goto catch; }
|
b + 1, kjv_book_string[b]); errno = EDOM; goto catch; }
|
||||||
x = lex(backing.a.data + build[b].offset);
|
x = lex(backing.data + build[b].offset);
|
||||||
while(lex_next_verse(&x)) {
|
while(lex_next_verse(&x)) {
|
||||||
const union kjvcite cite
|
const union kjvcite cite
|
||||||
= { .book = b, .chapter = x.chapter, .verse = x.verse };
|
= { .book = b, .chapter = x.chapter, .verse = x.verse };
|
||||||
|
56
src/source.h
56
src/source.h
@ -1,36 +1,29 @@
|
|||||||
#if defined BASE \
|
#include "pair.h" /* pair */
|
||||||
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- base */
|
|
||||||
#include "pair.h"
|
|
||||||
struct source { struct pair name, desc; };
|
struct source { struct pair name, desc; };
|
||||||
#endif /* base --> */
|
|
||||||
|
|
||||||
|
#define ARRAY_NAME sourcelist
|
||||||
|
#define ARRAY_TYPE struct source
|
||||||
|
#define ARRAY_HEAD
|
||||||
|
#include "../src/array.h"
|
||||||
|
|
||||||
#if defined PRIVATE \
|
#include <stddef.h> /* size_t */
|
||||||
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- private */
|
#include <stdint.h> /* uint32_t */
|
||||||
|
|
||||||
struct tree_source_node;
|
#define TABLE_NAME sourcemap
|
||||||
struct tree_source_tree { struct tree_source_node *node; unsigned height; };
|
#define TABLE_KEY struct pair
|
||||||
struct source_tree { struct tree_source_tree root; };
|
#define TABLE_UINT uint32_t
|
||||||
struct tree_source_ref { struct tree_source_node *node; unsigned height, idx; };
|
#define TABLE_VALUE size_t
|
||||||
struct tree_source_iterator {
|
#define TABLE_HEAD
|
||||||
struct tree_source_tree *root; struct tree_source_ref ref; int seen;
|
#include "../src/table.h"
|
||||||
};
|
|
||||||
struct source_tree_iterator { struct tree_source_iterator _; };
|
|
||||||
|
|
||||||
struct sourcelist_array { struct source *data; size_t size, capacity; };
|
#include "../src/journal.h" /* line64 */
|
||||||
|
|
||||||
struct sourcemap_bucket;
|
#define TREE_NAME source
|
||||||
struct sourcemap_table {
|
#define TREE_KEY union line64
|
||||||
struct sourcemap_bucket *buckets;
|
#define TREE_VALUE size_t
|
||||||
uint32_t log_capacity, size, top;
|
#define TREE_HEAD
|
||||||
};
|
#include "../src/tree.h"
|
||||||
|
|
||||||
#endif /* private --> */
|
|
||||||
|
|
||||||
|
|
||||||
#if defined PROTO \
|
|
||||||
|| !defined BASE && !defined PRIVATE && !defined PROTO /* <!-- proto */
|
|
||||||
#include <stddef.h>
|
|
||||||
struct sources {
|
struct sources {
|
||||||
struct sourcelist_array list;
|
struct sourcelist_array list;
|
||||||
struct sourcemap_table map;
|
struct sourcemap_table map;
|
||||||
@ -42,14 +35,3 @@ void sources_(struct sources *);
|
|||||||
int sources_is_empty(const struct sources *);
|
int sources_is_empty(const struct sources *);
|
||||||
const char *sources_to_string(const struct sources *);
|
const char *sources_to_string(const struct sources *);
|
||||||
const struct source *source_lookup(struct sources *s, const union line64 range);
|
const struct source *source_lookup(struct sources *s, const union line64 range);
|
||||||
#endif /* proto --> */
|
|
||||||
|
|
||||||
#ifdef BASE
|
|
||||||
#undef BASE
|
|
||||||
#endif
|
|
||||||
#ifdef PRIVATE
|
|
||||||
#undef PRIVATE
|
|
||||||
#endif
|
|
||||||
#ifdef PROTO
|
|
||||||
#undef PROTO
|
|
||||||
#endif
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
/** @license 2023 Neil Edelman, distributed under the terms of the
|
/** @license 2023 Neil Edelman, distributed under the terms of the
|
||||||
[MIT License](https://opensource.org/licenses/MIT).
|
[MIT License](https://opensource.org/licenses/MIT).
|
||||||
@std C11 */
|
@std C11 */
|
||||||
#define BASE
|
#include "../src/source.h"
|
||||||
#include "../src/source.h" /* base */
|
|
||||||
#include "../src/journal.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -22,6 +20,7 @@ static void sourcelist_to_string(const struct source *const s,
|
|||||||
#define ARRAY_NAME sourcelist
|
#define ARRAY_NAME sourcelist
|
||||||
#define ARRAY_TYPE struct source
|
#define ARRAY_TYPE struct source
|
||||||
#define ARRAY_TO_STRING
|
#define ARRAY_TO_STRING
|
||||||
|
#define ARRAY_BODY
|
||||||
#include "../src/array.h"
|
#include "../src/array.h"
|
||||||
|
|
||||||
|
|
||||||
@ -35,8 +34,9 @@ static uint32_t sourcemap_hash(const struct pair p) { return pair_djb2(p); }
|
|||||||
#define TABLE_KEY struct pair
|
#define TABLE_KEY struct pair
|
||||||
#define TABLE_UINT uint32_t
|
#define TABLE_UINT uint32_t
|
||||||
#define TABLE_VALUE size_t /* Index into source list. */
|
#define TABLE_VALUE size_t /* Index into source list. */
|
||||||
#define TABLE_DEFAULT 0
|
#define TABLE_DEFAULT 0 /* Default set at zero. */
|
||||||
#define TABLE_TO_STRING
|
#define TABLE_TO_STRING
|
||||||
|
#define TABLE_BODY
|
||||||
#include "../src/table.h"
|
#include "../src/table.h"
|
||||||
|
|
||||||
|
|
||||||
@ -51,13 +51,10 @@ static int source_compare(const union line64 a, const union line64 b)
|
|||||||
#define TREE_COMPARE
|
#define TREE_COMPARE
|
||||||
#define TREE_TO_STRING
|
#define TREE_TO_STRING
|
||||||
#define TREE_DEFAULT 0
|
#define TREE_DEFAULT 0
|
||||||
|
#define TREE_BODY
|
||||||
#include "../src/tree.h"
|
#include "../src/tree.h"
|
||||||
|
|
||||||
|
|
||||||
#define PROTO
|
|
||||||
#include "../src/source.h" /* proto */
|
|
||||||
|
|
||||||
|
|
||||||
/*!conditions:re2c*/
|
/*!conditions:re2c*/
|
||||||
|
|
||||||
static int scan(union date32 date, const char *const buffer,
|
static int scan(union date32 date, const char *const buffer,
|
||||||
|
88
src/table.h
88
src/table.h
@ -41,6 +41,11 @@
|
|||||||
`TABLE_EXPECT_TRAIT` and then subsequently including the name in
|
`TABLE_EXPECT_TRAIT` and then subsequently including the name in
|
||||||
`TABLE_TRAIT`.
|
`TABLE_TRAIT`.
|
||||||
|
|
||||||
|
@param[TABLE_HEAD, TABLE_BODY]
|
||||||
|
These go together to allow exporting non-static data between compilation units
|
||||||
|
by separating the `TABLE_BODY` refers to `TABLE_HEAD`, and identical
|
||||||
|
`TABLE_NAME`, `TABLE_KEY`, `TABLE_UNHASH`, `TABLE_VALUE`, and `TABLE_UINT`.
|
||||||
|
|
||||||
@fixme Remove entry as public struct, this should be entirely private.
|
@fixme Remove entry as public struct, this should be entirely private.
|
||||||
@fixme Why not have two `to_string` arguments on map? It's C, after all. This
|
@fixme Why not have two `to_string` arguments on map? It's C, after all. This
|
||||||
would be useful in some practical cases.
|
would be useful in some practical cases.
|
||||||
@ -56,6 +61,9 @@
|
|||||||
|| defined(TABLE_TRAIT) && !defined(TABLE_HAS_TO_STRING))
|
|| defined(TABLE_TRAIT) && !defined(TABLE_HAS_TO_STRING))
|
||||||
#error Test requires to string.
|
#error Test requires to string.
|
||||||
#endif
|
#endif
|
||||||
|
#if defined TABLE_HEAD && defined TABLE_BODY
|
||||||
|
#error Can not be TABLE_HEAD and TABLE_BODY.
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef TABLE_H /* <!-- idempotent */
|
#ifndef TABLE_H /* <!-- idempotent */
|
||||||
#define TABLE_H
|
#define TABLE_H
|
||||||
@ -99,6 +107,9 @@ static const char *const table_result_str[] = { TABLE_RESULT };
|
|||||||
#ifndef TABLE_UINT
|
#ifndef TABLE_UINT
|
||||||
#define TABLE_UINT size_t
|
#define TABLE_UINT size_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef TABLE_BODY /* <!-- head */
|
||||||
|
|
||||||
/** <typedef:<PN>hash_fn> returns this hash type by `TABLE_UINT`, which must be
|
/** <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
|
be an unsigned integer. Places a simplifying limit on the maximum number of
|
||||||
elements of half the cardinality. */
|
elements of half the cardinality. */
|
||||||
@ -160,6 +171,39 @@ struct PN_(bucket) {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 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/table/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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* In no particular order, usually, but deterministic up to topology changes. */
|
||||||
|
struct PN_(iterator) { struct N_(table) *table; PN_(uint) i; };
|
||||||
|
|
||||||
|
/** ![States](../doc/table/it.png)
|
||||||
|
|
||||||
|
Adding, deleting, successfully looking up entries, or any modification of the
|
||||||
|
table's topology invalidates the iterator.
|
||||||
|
Iteration usually not in any particular order. The asymptotic runtime of
|
||||||
|
iterating though the whole table is proportional to the capacity. */
|
||||||
|
struct N_(table_iterator);
|
||||||
|
struct N_(table_iterator) { struct PN_(iterator) _; };
|
||||||
|
|
||||||
|
#endif /* head --> */
|
||||||
|
#ifndef TABLE_HEAD /* <!-- body */
|
||||||
|
|
||||||
/** Gets the key of an occupied `bucket`. */
|
/** Gets the key of an occupied `bucket`. */
|
||||||
static PN_(key) PN_(bucket_key)(const struct PN_(bucket) *const bucket) {
|
static PN_(key) PN_(bucket_key)(const struct PN_(bucket) *const bucket) {
|
||||||
assert(bucket && bucket->next != TABLE_NULL);
|
assert(bucket && bucket->next != TABLE_NULL);
|
||||||
@ -182,24 +226,6 @@ static PN_(value) PN_(bucket_value)(const struct PN_(bucket) *const bucket) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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/table/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. */
|
/** The capacity of a non-idle `table` is always a power-of-two. */
|
||||||
static PN_(uint) PN_(capacity)(const struct N_(table) *const table)
|
static PN_(uint) PN_(capacity)(const struct N_(table) *const table)
|
||||||
{ return assert(table && table->buckets && table->log_capacity >= 3),
|
{ return assert(table && table->buckets && table->log_capacity >= 3),
|
||||||
@ -487,8 +513,6 @@ static enum table_result PN_(put_key)(struct N_(table) *const table,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In no particular order, usually, but deterministic up to topology changes. */
|
|
||||||
struct PN_(iterator) { struct N_(table) *table; PN_(uint) i; };
|
|
||||||
/** @return Before `table`. */
|
/** @return Before `table`. */
|
||||||
static struct PN_(iterator) PN_(iterator)(struct N_(table) *const table)
|
static struct PN_(iterator) PN_(iterator)(struct N_(table) *const table)
|
||||||
{ struct PN_(iterator) it; it.table = table, it.i = 0, it.i--; return it; }
|
{ struct PN_(iterator) it; it.table = table, it.i = 0, it.i--; return it; }
|
||||||
@ -539,15 +563,6 @@ static int PN_(remove)(struct PN_(iterator) *const it) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ![States](../doc/table/it.png)
|
|
||||||
|
|
||||||
Adding, deleting, successfully looking up entries, or any modification of the
|
|
||||||
table's topology invalidates the iterator.
|
|
||||||
Iteration usually not in any particular order. The asymptotic runtime of
|
|
||||||
iterating though the whole table is proportional to the capacity. */
|
|
||||||
struct N_(table_iterator);
|
|
||||||
struct N_(table_iterator) { struct PN_(iterator) _; };
|
|
||||||
|
|
||||||
/** Zeroed data (not all-bits-zero) is initialized. @return An idle array.
|
/** Zeroed data (not all-bits-zero) is initialized. @return An idle array.
|
||||||
@order \Theta(1) @allow */
|
@order \Theta(1) @allow */
|
||||||
static struct N_(table) N_(table)(void) {
|
static struct N_(table) N_(table)(void) {
|
||||||
@ -790,6 +805,8 @@ static void PN_(unused_base)(void) {
|
|||||||
}
|
}
|
||||||
static void PN_(unused_base_coda)(void) { PN_(unused_base)(); }
|
static void PN_(unused_base_coda)(void) { PN_(unused_base)(); }
|
||||||
|
|
||||||
|
#endif /* body --> */
|
||||||
|
|
||||||
#endif /* base code --> */
|
#endif /* base code --> */
|
||||||
|
|
||||||
|
|
||||||
@ -802,13 +819,6 @@ static void PN_(unused_base_coda)(void) { PN_(unused_base)(); }
|
|||||||
#define NT_(n) N_(n)
|
#define NT_(n) N_(n)
|
||||||
#endif /* !trait --> */
|
#endif /* !trait --> */
|
||||||
|
|
||||||
/* #ifdef TABLE_TRAIT
|
|
||||||
#define N_D_(n, m) TABLE_CAT(N_(n), TABLE_CAT(TABLE_TRAIT, m))
|
|
||||||
#else
|
|
||||||
#define N_D_(n, m) TABLE_CAT(N_(n), m)
|
|
||||||
#endif
|
|
||||||
#define PN_D_(n, m) TABLE_CAT(table, N_D_(n, m)) */
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef TABLE_TO_STRING /* <!-- to string trait */
|
#ifdef TABLE_TO_STRING /* <!-- to string trait */
|
||||||
/** Thunk `b` -> `a`. */
|
/** Thunk `b` -> `a`. */
|
||||||
@ -897,6 +907,12 @@ static void PN_D_(unused, default_coda)(void) { PN_D_(unused, default)(); }
|
|||||||
#ifdef TABLE_TEST
|
#ifdef TABLE_TEST
|
||||||
#undef TABLE_TEST
|
#undef TABLE_TEST
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TABLE_BODY
|
||||||
|
#undef TABLE_BODY
|
||||||
|
#endif
|
||||||
|
#ifdef TABLE_HEAD
|
||||||
|
#undef TABLE_HEAD
|
||||||
|
#endif
|
||||||
#endif /* done --> */
|
#endif /* done --> */
|
||||||
#ifdef TABLE_TRAIT
|
#ifdef TABLE_TRAIT
|
||||||
#undef TABLE_TRAIT
|
#undef TABLE_TRAIT
|
||||||
|
19
src/text.c
19
src/text.c
@ -1,18 +1,22 @@
|
|||||||
/** Dynamic contiguous string that is used to load files. */
|
/** Dynamic contiguous string that is used to load files. */
|
||||||
|
|
||||||
|
#include "text.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define ARRAY_NAME char
|
#define ARRAY_NAME char
|
||||||
#define ARRAY_TYPE char
|
#define ARRAY_TYPE char
|
||||||
|
#define ARRAY_BODY
|
||||||
#include "../src/array.h"
|
#include "../src/array.h"
|
||||||
#define HAVE_CHAR_ARRAY
|
|
||||||
#include "text.h"
|
struct char_array text(void) { return char_array(); }
|
||||||
#include <stdio.h>
|
void text_(struct char_array *const text) { char_array_(text); }
|
||||||
|
|
||||||
/** Append a text file, `fn`, to `c`, and add a '\0'.
|
/** 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
|
@return The start of the appended file or null on error. A partial read is a
|
||||||
failure. @throws[fopen, fread, malloc]
|
failure. @throws[fopen, fread, malloc]
|
||||||
@throws[EISEQ] The text file has embedded nulls.
|
@throws[EISEQ] The text file has embedded nulls.
|
||||||
@throws[ERANGE] If the standard library does not follow POSIX. */
|
@throws[ERANGE] If the standard library does not follow POSIX? */
|
||||||
static char *append_file(struct char_array *text, const char *const fn) {
|
char *text_append_file(struct char_array *text, const char *const fn) {
|
||||||
FILE *fp = 0;
|
FILE *fp = 0;
|
||||||
const size_t granularity = 1024;
|
const size_t granularity = 1024;
|
||||||
size_t nread, start;
|
size_t nread, start;
|
||||||
@ -40,8 +44,3 @@ finally:
|
|||||||
if(fp) fclose(fp);
|
if(fp) fclose(fp);
|
||||||
return success ? text->data + start : 0;
|
return success ? text->data + start : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct text text(void) { struct text text; text.a = char_array(); return text; }
|
|
||||||
void text_(struct text *const text) { char_array_(&text->a); }
|
|
||||||
char *text_append_file(struct text *text, const char *const fn)
|
|
||||||
{ return append_file(&text->a, fn); }
|
|
||||||
|
17
src/text.h
17
src/text.h
@ -1,11 +1,8 @@
|
|||||||
#include <stddef.h>
|
#define ARRAY_NAME char
|
||||||
|
#define ARRAY_TYPE char
|
||||||
|
#define ARRAY_HEAD
|
||||||
|
#include "../src/array.h"
|
||||||
|
|
||||||
#ifndef HAVE_CHAR_ARRAY
|
struct char_array text(void);
|
||||||
struct char_array { char *data; size_t size, capacity; };
|
void text_(struct char_array *);
|
||||||
#endif
|
char *text_append_file(struct char_array *, const char *);
|
||||||
|
|
||||||
struct text { struct char_array a; };
|
|
||||||
|
|
||||||
struct text text(void);
|
|
||||||
void text_(struct text *);
|
|
||||||
char *text_append_file(struct text *, const char *);
|
|
||||||
|
84
src/tree.h
84
src/tree.h
@ -44,6 +44,11 @@
|
|||||||
Named traits are obtained by including `tree.h` multiple times with
|
Named traits are obtained by including `tree.h` multiple times with
|
||||||
`TREE_EXPECT_TRAIT` and then subsequently including the name in `TREE_TRAIT`.
|
`TREE_EXPECT_TRAIT` and then subsequently including the name in `TREE_TRAIT`.
|
||||||
|
|
||||||
|
@param[TREE_HEAD, TREE_BODY]
|
||||||
|
These go together to allow exporting non-static data between compilation units
|
||||||
|
by separating the `TABLE_BODY` refers to `TABLE_HEAD`, and identical
|
||||||
|
`TREE_NAME`, `TREE_KEY`, `TREE_VALUE`, and `TREE_ORDER`.
|
||||||
|
|
||||||
@fixme merge, difference
|
@fixme merge, difference
|
||||||
|
|
||||||
@std C89 */
|
@std C89 */
|
||||||
@ -58,6 +63,9 @@
|
|||||||
|| defined(TREE_TRAIT) && !defined(TREE_HAS_TO_STRING))
|
|| defined(TREE_TRAIT) && !defined(TREE_HAS_TO_STRING))
|
||||||
#error Test requires to string.
|
#error Test requires to string.
|
||||||
#endif
|
#endif
|
||||||
|
#if defined TREE_HEAD && defined TREE_BODY
|
||||||
|
#error Can not be TREE_HEAD and TREE_BODY.
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef TREE_H /* <!-- idempotent */
|
#ifndef TREE_H /* <!-- idempotent */
|
||||||
#define TREE_H
|
#define TREE_H
|
||||||
@ -114,6 +122,8 @@ struct tree_node_count { size_t branches, leaves; };
|
|||||||
#define TREE_KEY unsigned
|
#define TREE_KEY unsigned
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef TREE_BODY /* <!-- head */
|
||||||
|
|
||||||
/** Ordered type used by <typedef:<PB>compare_fn>; defaults to `unsigned`. */
|
/** Ordered type used by <typedef:<PB>compare_fn>; defaults to `unsigned`. */
|
||||||
typedef TREE_KEY PB_(key);
|
typedef TREE_KEY PB_(key);
|
||||||
|
|
||||||
@ -123,20 +133,6 @@ typedef TREE_KEY PB_(key);
|
|||||||
typedef TREE_VALUE PB_(value);
|
typedef TREE_VALUE PB_(value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Returns a positive result if `a` is out-of-order with respect to `b`,
|
|
||||||
inducing a strict weak order. This is compatible, but less strict then the
|
|
||||||
comparators from `bsearch` and `qsort`; it only needs to divide entries into
|
|
||||||
two instead of three categories. */
|
|
||||||
typedef int (*PB_(compare_fn))(const PB_(key) a, const PB_(key) b);
|
|
||||||
#ifndef TREE_COMPARE /* <!-- !cmp */
|
|
||||||
/** The default `TREE_COMPARE` on `a` and `b` is integer comparison that
|
|
||||||
results in ascending order, `a > b`. Use `TREE_COMPARE` to supply one's own.
|
|
||||||
@implements <typedef:<PB>compare_fn> */
|
|
||||||
static int B_(compare)(const PB_(key) a, const PB_(key) b)
|
|
||||||
{ return a > b; }
|
|
||||||
#define TREE_COMPARE &PB_(default_compare)
|
|
||||||
#endif /* !cmp --> */
|
|
||||||
|
|
||||||
/* These rules are more lazy than the original so as to not exhibit worst-case
|
/* These rules are more lazy than the original so as to not exhibit worst-case
|
||||||
behaviour in small trees, as <Johnson, Shasha, 1993, Free-at-Empty>, (lookup
|
behaviour in small trees, as <Johnson, Shasha, 1993, Free-at-Empty>, (lookup
|
||||||
is potentially slower after deleting.) In the terminology of
|
is potentially slower after deleting.) In the terminology of
|
||||||
@ -165,14 +161,7 @@ struct PB_(node) {
|
|||||||
};
|
};
|
||||||
/* B-tree branch is a <tag:<PB>node> and links to `size + 1` nodes. */
|
/* B-tree branch is a <tag:<PB>node> and links to `size + 1` nodes. */
|
||||||
struct PB_(branch) { struct PB_(node) base, *child[TREE_ORDER]; };
|
struct PB_(branch) { struct PB_(node) base, *child[TREE_ORDER]; };
|
||||||
/** @return Downcasts `as_leaf` to a branch. */
|
|
||||||
static struct PB_(branch) *PB_(as_branch)(struct PB_(node) *const as_leaf)
|
|
||||||
{ return (struct PB_(branch) *)(void *)
|
|
||||||
((char *)as_leaf - offsetof(struct PB_(branch), base)); }
|
|
||||||
/** @return Downcasts `as_node` to a branch. */
|
|
||||||
static const struct PB_(branch) *PB_(as_branch_c)(const struct PB_(node) *
|
|
||||||
const as_node) { return (const struct PB_(branch) *)(const void *)
|
|
||||||
((const char *)as_node - offsetof(struct PB_(branch), base)); }
|
|
||||||
/* Node plus height is a [sub]-tree. */
|
/* Node plus height is a [sub]-tree. */
|
||||||
struct PB_(tree) { struct PB_(node) *node; unsigned height; };
|
struct PB_(tree) { struct PB_(node) *node; unsigned height; };
|
||||||
/** To initialize it to an idle state, see <fn:<B>tree>, `{0}` (`C99`), or
|
/** To initialize it to an idle state, see <fn:<B>tree>, `{0}` (`C99`), or
|
||||||
@ -189,6 +178,42 @@ struct PB_(ref) {
|
|||||||
struct PB_(node) *node; /* If null, others ignored. */
|
struct PB_(node) *node; /* If null, others ignored. */
|
||||||
unsigned height, idx; /* `idx < node.size` means valid. */
|
unsigned height, idx; /* `idx < node.size` means valid. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) ref; };
|
||||||
|
|
||||||
|
/** Adding, deleting, or changes in the topology of the tree invalidate the
|
||||||
|
iterator. To modify the tree while iterating, take the <fn:<B>tree_key> and
|
||||||
|
restart the iterator with <fn:<B>tree_less> or <fn:<B>tree_more> as
|
||||||
|
appropriate. */
|
||||||
|
struct B_(tree_iterator);
|
||||||
|
struct B_(tree_iterator) { struct PB_(iterator) _; };
|
||||||
|
|
||||||
|
#endif /* head --> */
|
||||||
|
#ifndef TREE_HEAD /* <!-- body */
|
||||||
|
|
||||||
|
/** Returns a positive result if `a` is out-of-order with respect to `b`,
|
||||||
|
inducing a strict weak order. This is compatible, but less strict then the
|
||||||
|
comparators from `bsearch` and `qsort`; it only needs to divide entries into
|
||||||
|
two instead of three categories. */
|
||||||
|
typedef int (*PB_(compare_fn))(const PB_(key) a, const PB_(key) b);
|
||||||
|
#ifndef TREE_COMPARE /* <!-- !cmp */
|
||||||
|
/** The default `TREE_COMPARE` on `a` and `b` is integer comparison that
|
||||||
|
results in ascending order, `a > b`. Use `TREE_COMPARE` to supply one's own.
|
||||||
|
@implements <typedef:<PB>compare_fn> */
|
||||||
|
static int B_(compare)(const PB_(key) a, const PB_(key) b)
|
||||||
|
{ return a > b; }
|
||||||
|
#define TREE_COMPARE &PB_(default_compare)
|
||||||
|
#endif /* !cmp --> */
|
||||||
|
|
||||||
|
/** @return Downcasts `as_leaf` to a branch. */
|
||||||
|
static struct PB_(branch) *PB_(as_branch)(struct PB_(node) *const as_leaf)
|
||||||
|
{ return (struct PB_(branch) *)(void *)
|
||||||
|
((char *)as_leaf - offsetof(struct PB_(branch), base)); }
|
||||||
|
/** @return Downcasts `as_node` to a branch. */
|
||||||
|
static const struct PB_(branch) *PB_(as_branch_c)(const struct PB_(node) *
|
||||||
|
const as_node) { return (const struct PB_(branch) *)(const void *)
|
||||||
|
((const char *)as_node - offsetof(struct PB_(branch), base)); }
|
||||||
|
|
||||||
#ifdef TREE_VALUE /* <!-- value */
|
#ifdef TREE_VALUE /* <!-- value */
|
||||||
/** Gets the value of `ref`. */
|
/** Gets the value of `ref`. */
|
||||||
static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
|
static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
|
||||||
@ -200,7 +225,6 @@ static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
|
|||||||
{ return ref.node ? ref.node->key + ref.idx : 0; }
|
{ return ref.node ? ref.node->key + ref.idx : 0; }
|
||||||
#endif /* !value --> */
|
#endif /* !value --> */
|
||||||
|
|
||||||
struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) ref; };
|
|
||||||
/** Iterator for `tree` in empty state. */
|
/** Iterator for `tree` in empty state. */
|
||||||
static struct PB_(iterator) PB_(iterator)(struct B_(tree) *const tree) {
|
static struct PB_(iterator) PB_(iterator)(struct B_(tree) *const tree) {
|
||||||
struct PB_(iterator) it;
|
struct PB_(iterator) it;
|
||||||
@ -1480,12 +1504,6 @@ finally:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Adding, deleting, or changes in the topology of the tree invalidate the
|
|
||||||
iterator. To modify the tree while iterating, take the <fn:<B>tree_key> and
|
|
||||||
restart the iterator with <fn:<B>tree_less> or <fn:<B>tree_more> as
|
|
||||||
appropriate. */
|
|
||||||
struct B_(tree_iterator);
|
|
||||||
struct B_(tree_iterator) { struct PB_(iterator) _; };
|
|
||||||
/** @return Cursor at null in valid `tree`. @order \Theta(1) @allow */
|
/** @return Cursor at null in valid `tree`. @order \Theta(1) @allow */
|
||||||
static struct B_(tree_iterator) B_(tree_iterator)(struct B_(tree) *const tree)
|
static struct B_(tree_iterator) B_(tree_iterator)(struct B_(tree) *const tree)
|
||||||
{ struct B_(tree_iterator) it; it._ = PB_(iterator)(tree); return it; }
|
{ struct B_(tree_iterator) it; it._ = PB_(iterator)(tree); return it; }
|
||||||
@ -1565,6 +1583,8 @@ static void PB_(unused_base_coda)(void) { PB_(unused_base)(); }
|
|||||||
#define BOX_MAJOR_NAME tree
|
#define BOX_MAJOR_NAME tree
|
||||||
#define BOX_MINOR_NAME TREE_NAME
|
#define BOX_MINOR_NAME TREE_NAME
|
||||||
|
|
||||||
|
#endif /* body --> */
|
||||||
|
|
||||||
#endif /* base code --> */
|
#endif /* base code --> */
|
||||||
|
|
||||||
|
|
||||||
@ -1660,6 +1680,12 @@ static void PB_D_(unused, default_coda)(void) { PB_D_(unused, default)(); }
|
|||||||
#ifdef TREE_TEST
|
#ifdef TREE_TEST
|
||||||
#undef TREE_TEST
|
#undef TREE_TEST
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef TREE_BODY
|
||||||
|
#undef TREE_BODY
|
||||||
|
#endif
|
||||||
|
#ifdef TREE_HEAD
|
||||||
|
#undef TREE_HEAD
|
||||||
|
#endif
|
||||||
#endif /* done --> */
|
#endif /* done --> */
|
||||||
#ifdef TREE_TRAIT
|
#ifdef TREE_TRAIT
|
||||||
#undef TREE_TRAIT
|
#undef TREE_TRAIT
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#include "../src/journal.h"
|
|
||||||
#include "../src/source.h"
|
#include "../src/source.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct text t = text();
|
struct char_array t = text();
|
||||||
char *content;
|
char *content;
|
||||||
int success = EXIT_SUCCESS;
|
int success = EXIT_SUCCESS;
|
||||||
errno = 0; /* `errno` is not set correctly to 0 in some debug situations. */
|
errno = 0; /* `errno` is not set correctly to 0 in some debug situations. */
|
||||||
|
Loading…
Reference in New Issue
Block a user