soucemap pair -> size_t doesn't make sense; I think. Should be line64 -> size_t?

This commit is contained in:
Neil 2023-02-11 21:21:29 -08:00
parent fbabdf9810
commit b69e40bc44
10 changed files with 296 additions and 514 deletions

View File

@ -86,46 +86,37 @@ typedef ARRAY_TYPE PA_(type);
struct A_(array) { PA_(type) *data; size_t size, capacity; };
/* !data -> !size, data -> capacity >= min && size <= capacity <= max */
/* Size 3 iterator: |_ |_ |_ |. Size 0: |. `size` and `!up` is invalid. */
struct PA_(iterator) { struct A_(array) *a; size_t i; int seen; };
/** @return Initialize before beginning of a valid `a`. */
static struct PA_(iterator) PA_(begin)(struct A_(array) *const a) {
/* `a` non-null; `i >= elements` empty; insert-delete on left like C++. */
struct PA_(iterator) { struct A_(array) *a; size_t i; };
/** @return Iterator at end of (non-null) valid `a`. */
static struct PA_(iterator) PA_(iterator)(struct A_(array) *const a) {
struct PA_(iterator) it;
assert(a);
it.a = a, it.i = 0, it.seen = 0;
assert(a), it.a = a, it.i = (size_t)~0;
return it;
}
/** @return Initialize after the end of a valid `a`. */
static struct PA_(iterator) PA_(end)(struct A_(array) *const a) {
struct PA_(iterator) it;
assert(a);
it.a = a, it.i = a->size, it.seen = 0;
return it;
}
/** @return Iterator before element `i` of `a`. */
/** @return Iterator at element `i` of non-null `a`. */
static struct PA_(iterator) PA_(iterator_at)(struct A_(array) *a, size_t i) {
struct PA_(iterator) it;
assert(a);
it.a = a, it.i = i > a->size ? a->size : i, it.seen = 0; return it;
assert(a), it.a = a, it.i = i < a->size ? i : (size_t)~0;
return it;
}
/** @return Whether it moved `it` forwards and picked-up `e`. */
static int PA_(next)(struct PA_(iterator) *const it, PA_(type) **const e) {
/** @return Dereference the element pointed to by valid `it`. */
static PA_(type) *PA_(element)(struct PA_(iterator) *const it)
{ return it->a->data + it->i; }
/** Next `it`. @return Valid element? */
static int PA_(next)(struct PA_(iterator) *const it) {
assert(it && it->a);
it->i += !!it->seen, it->seen = 1;
if(it->i >= it->a->size) return it->i = it->a->size, it->seen = 0, 0;
if(e) *e = it->a->data + it->i;
return 1;
if(it->i >= it->a->size) it->i = (size_t)~0;
return ++it->i < it->a->size;
}
/** @return Whether it moved `it` backwards and picked-up `e`. */
static int PA_(previous)(struct PA_(iterator) *const it, PA_(type) **const e) {
/** Previous `it`. @return Valid element? */
static int PA_(previous)(struct PA_(iterator) *const it) {
assert(it && it->a);
if(it->i > it->a->size) it->i = it->a->size, it->seen = 0; /* Clip. */
if(!it->i) return it->seen = 0, 0; /* First. */
it->i--, it->seen = 1;
if(e) *e = it->a->data + it->i;
return 1;
if(it->i > it->a->size) it->i = it->a->size; /* Clip. */
return --it->i < it->a->size;
}
/* fixme: static int PA_(remove)(struct PA_(iterator) *const it) */
/* fixme: static struct PA_(iterator)
PA_(remove)(struct PA_(iterator) *const 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` */
@ -148,24 +139,6 @@ 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 An iterator before the start 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 before `idx` of `a`. */
static struct A_(array_iterator) A_(array_at)(struct A_(array) *a, size_t idx)
{ struct A_(array_iterator) it; it._ = PA_(iterator_at)(a, idx); return it; }
/** Move initialized `it` to the next cursor position. @return Pointer to the
element through which it moved, or null if at the end. */
static PA_(type) *A_(array_next)(struct A_(array_iterator) *const it)
{ PA_(type) *v; return assert(it), PA_(next)(&it->_, &v) ? v : 0; }
/** Move initialized `it` to the previous cursor position. @return Pointer to
the element through which it moved, or null if at the beginning. */
static PA_(type) *A_(array_previous)(struct A_(array_iterator) *const it)
{ PA_(type) *v; return assert(it), PA_(previous)(&it->_, &v) ? v : 0; }
/** 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`
@ -336,10 +309,10 @@ static int A_(array_splice)(struct A_(array) *restrict const a,
static void PA_(unused_base_coda)(void);
static void PA_(unused_base)(void) {
PA_(iterator)(0); PA_(iterator_at)(0, 0); PA_(element)(0);
PA_(next)(0); PA_(previous)(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_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);

View File

@ -22,9 +22,8 @@ struct tree_day_node;
struct tree_day_tree { struct tree_day_node *node; unsigned height; };
struct day_tree { struct tree_day_tree root; };
struct tree_day_ref { struct tree_day_node *node; unsigned height, idx; };
struct tree_day_iterator {
struct tree_day_tree *root; struct tree_day_ref ref; int seen;
};
struct tree_day_iterator
{ struct tree_day_tree *root; struct tree_day_ref ref; };
struct day_tree_iterator { struct tree_day_iterator _; };
#endif /* generic --> */
@ -38,7 +37,7 @@ struct journal journal(void);
void journal_(struct journal *);
int journal_is_valid(const struct journal *);
const char *journal_to_string(const struct journal *);
struct journal_iterator journal_begin(struct journal *const j);
struct journal_iterator journal_iterator(struct journal *const j);
int journal_next(struct journal_iterator *, union date32 *, const char **);
#endif /* proto --> */

View File

@ -204,11 +204,12 @@ struct journal journal(void) {
/* Structure is now stable because we aren't going to move it;
convert all of offsets back to pointers. */
it = day_tree_begin(&j.days);
while(day_tree_next(&it, 0, &v.text)) {
/*printf("[%zu]...", *v.offset);*/
it = day_tree_iterator(&j.days);
while(day_tree_next(&it)) {
v.text = day_tree_value(&it);
printf("[%zu]...", *v.offset);
*v.text = j.backing.a.data + *v.offset;
/*printf("<%.32s>\n", *v.text);*/
printf("<%.32s>\n", *v.text);
}
/*fprintf(stderr, "Journal has entries: %s\n",
day_tree_to_string(&j.days));*/
@ -234,9 +235,9 @@ int journal_is_valid(const struct journal *const j) {
const char *journal_to_string(const struct journal *const j)
{ return day_tree_to_string(&j->days); }
struct journal_iterator journal_begin(struct journal *const j) {
struct journal_iterator journal_iterator(struct journal *const j) {
struct journal_iterator it;
it._ = day_tree_begin(&j->days);
it._ = day_tree_iterator(&j->days);
return it;
}
@ -249,8 +250,9 @@ struct journal_iterator journal_begin(struct journal *const j) {
int journal_next(struct journal_iterator *const it,
union date32 *const k, const char **v) {
const char **needless_modifiable;
if(!day_tree_next(&it->_, k, &needless_modifiable)) return 0;
*v = *needless_modifiable;
assert(it && k && v);
if(!day_tree_next(&it->_)) return 0;
*k = day_tree_key(&it->_);
*v = *day_tree_value(&it->_);
return 1;
}

View File

@ -196,7 +196,7 @@ int main(void) {
"set output \"kjv.eps\"\n"
"$Data <<EOD\n"
"# date\tverse\tset\tcumulative / %zu\n", bible.words.total);
it = journal_begin(&j);
it = journal_iterator(&j);
while(journal_next(&it, &k, &v)) if(!scan(k, v, &bible)) goto catch;
printf("EOD\n"
"set monochrome\n"

View File

@ -40,8 +40,7 @@ struct source *sources_add(struct sources *, const union line64);
struct sources sources(struct journal *);
void sources_(struct sources *);
const char *sources_to_string(const struct sources *);
const struct source *source_lookup(const struct sources *s,
const union line64 range);
const struct source *source_lookup(struct sources *s, const union line64 range);
#endif /* proto --> */
#ifdef BASE

View File

@ -50,7 +50,7 @@ static int source_compare(const union line64 a, const union line64 b)
#define TREE_VALUE size_t /* Index into source list. */
#define TREE_COMPARE
#define TREE_TO_STRING
#define TREE_DEFAULT 0
#define TREE_DEFAULT (size_t)0
#include "../src/tree.h"
@ -157,7 +157,7 @@ struct sources sources(struct journal *const j) {
if(!(nul = sourcelist_array_new(&s.list))) goto catch;
nul->name.a = nul->name.b = nul->desc.a = nul->desc.b = 0;
}
it = journal_begin(j);
it = journal_iterator(j);
while(journal_next(&it, &k, &v)) if(!scan(k, v, &s)) goto catch;
fprintf(stderr, "List of sources: %s.\n"
"Mapped to indices: %s.\n"
@ -172,10 +172,11 @@ finally:
/** Lookup the last source in `range` in sources `s`. They are invalidated on
adding a source, (probably fine.) */
const struct source *source_lookup(const struct sources *const s,
const struct source *source_lookup(struct sources *const s,
const union line64 range) {
size_t idx;
union line64 line_source;
assert(s);
idx = source_tree_left(&s->dates, range);
line_source = source_tree_less_or(&s->dates, range, (union line64){0});
sourcemap_table_get(&s->map, line_source);
return s->list.data + idx;
}

View File

@ -17,7 +17,7 @@
<typedef:<PN>key> associated therewith; required. `<PN>` is private, whose
names are prefixed in a manner to avoid collisions.
@param[TABLE_INVERSE]
@param[TABLE_UNHASH]
By default it assumes that `<N>is_equal` is supplied; with this, instead
requires `<N>unhash` satisfying <typedef:<PN>unhash_fn>.
@ -26,7 +26,7 @@
associative array.
@param[TABLE_UINT]
This is <typedef:<PN>uint>, the unsigned type of hash hash of the key given by
This is <typedef:<PN>uint>, the unsigned type of hash of the key given by
<typedef:<PN>hash_fn>; defaults to `size_t`.
@param[TABLE_DEFAULT]
@ -76,9 +76,9 @@
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_HIGH ((TABLE_M1 >> 1) + 1) /* High-bit set: max cardinality. */
#define TABLE_END (TABLE_HIGH) /* Out-of-band signalling end of chain. */
#define TABLE_NULL (TABLE_HIGH + 1) /* Out-of-band signalling no item. */
#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.
@ -104,7 +104,7 @@ static const char *const table_result_str[] = { TABLE_RESULT };
elements of half the cardinality. */
typedef TABLE_UINT PN_(uint);
/** Valid tag type defined by `TABLE_KEY` used for keys. If `TABLE_INVERSE` is
/** Valid tag type defined by `TABLE_KEY` used for keys. If `TABLE_UNHASH` 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%? */
@ -115,8 +115,8 @@ typedef const TABLE_KEY PN_(key_c); /* Works 90%? */
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
#ifdef TABLE_UNHASH /* <!-- inv */
/** Defining `TABLE_UNHASH` 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. */
@ -124,7 +124,7 @@ 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
If `TABLE_UNHASH` 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 --> */
@ -152,7 +152,7 @@ typedef PN_(key) PN_(entry);
struct PN_(bucket) {
PN_(uint) next; /* Bucket index, including `TABLE_NULL` and `TABLE_END`. */
PN_(uint) hash;
#ifndef TABLE_INVERSE
#ifndef TABLE_UNHASH
PN_(key) key;
#endif
#ifdef TABLE_VALUE
@ -163,8 +163,8 @@ struct PN_(bucket) {
/** 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. */
#ifdef TABLE_UNHASH
/* On `TABLE_UNHASH`, this function must be defined by the user. */
return N_(unhash)(bucket->hash);
#else
return bucket->key;
@ -205,23 +205,23 @@ 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,
/** @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>chain_head> not equal
to the index (another open bucket). */
static PN_(uint) PN_(chain_head)(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`) */
exists, (by restarting and going though the list.)
@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);
for(next = PN_(chain_head)(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;
@ -246,7 +246,7 @@ static void PN_(force_stack)(struct N_(table) *const table) {
top &= ~TABLE_HIGH;
do bucket = table->buckets + ++top/*, assert(top < capacity)*/;
while(bucket->next != TABLE_NULL
&& PN_(to_bucket_no)(table, bucket->hash) == top);
&& PN_(chain_head)(table, bucket->hash) == top);
table->top = top; /* Eager. */
}
}
@ -287,10 +287,10 @@ static void PN_(move_to_top)(struct N_(table) *const table, const PN_(uint) m) {
}
/* stack --> */
/** `TABLE_INVERSE` is injective, so in that case, we only compare hashes.
/** `TABLE_UNHASH` 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
#ifdef TABLE_UNHASH
return (void)a, (void)b, 1;
#else
/* Must have this function declared. */
@ -304,11 +304,11 @@ static struct PN_(bucket) *PN_(query)(struct N_(table) *const table,
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));
bucket1 = table->buckets + (head = b1 = PN_(chain_head)(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;
&& b1 != PN_(chain_head)(table, bucket1->hash)) return 0;
while(hash != bucket1->hash
|| !PN_(equal_buckets)(key, PN_(bucket_key)(bucket1))) {
if(b2 == TABLE_END) return 0;
@ -377,7 +377,7 @@ static int PN_(buffer)(struct N_(table) *const table, const PN_(uint) n) {
PN_(uint) g, hash;
idx = table->buckets + i;
if(idx->next == TABLE_NULL) continue;
g = PN_(to_bucket_no)(table, hash = idx->hash);
g = PN_(chain_head)(table, hash = idx->hash);
/* It's a power-of-two size, so, like consistent hashing, `E[old/new]`
capacity that a closed bucket will remain where it is. */
if(i == g) { idx->next = TABLE_END; continue; }
@ -387,7 +387,7 @@ static int PN_(buffer)(struct N_(table) *const table, const PN_(uint) n) {
PN_(uint) h = g & ~mask; assert(h <= g);
if(h < g && i < h
&& (head = table->buckets + h, assert(head->next != TABLE_NULL),
PN_(to_bucket_no)(table, head->hash) == g)) {
PN_(chain_head)(table, head->hash) == g)) {
memcpy(go, head, sizeof *head);
go->next = TABLE_END, head->next = TABLE_NULL;
/* Fall-though -- the bucket still needs to be put on wait. */
@ -404,7 +404,7 @@ static int PN_(buffer)(struct N_(table) *const table, const PN_(uint) n) {
/* Search waiting stack for buckets that moved concurrently. */
{ PN_(uint) prev = TABLE_END, w = wait; while(w != TABLE_END) {
struct PN_(bucket) *waiting = table->buckets + w;
PN_(uint) cl = PN_(to_bucket_no)(table, waiting->hash);
PN_(uint) cl = PN_(chain_head)(table, waiting->hash);
struct PN_(bucket) *const closed = table->buckets + cl;
assert(cl != w);
if(closed->next == TABLE_NULL) {
@ -421,7 +421,7 @@ static int PN_(buffer)(struct N_(table) *const table, const PN_(uint) n) {
/* Rebuild the top stack at the high numbers from the waiting at low. */
while(wait != TABLE_END) {
struct PN_(bucket) *const waiting = table->buckets + wait;
PN_(uint) h = PN_(to_bucket_no)(table, waiting->hash);
PN_(uint) h = PN_(chain_head)(table, waiting->hash);
struct PN_(bucket) *const head = table->buckets + h;
struct PN_(bucket) *top;
assert(h != wait && head->next != TABLE_NULL);
@ -439,7 +439,7 @@ static void PN_(replace_key)(struct PN_(bucket) *const bucket,
const PN_(key) key, const PN_(uint) hash) {
(void)key;
bucket->hash = hash;
#ifndef TABLE_INVERSE
#ifndef TABLE_UNHASH
bucket->key = key;
#endif
}
@ -451,9 +451,9 @@ static struct PN_(bucket) *PN_(evict)(struct N_(table) *const table,
PN_(uint) i;
struct PN_(bucket) *bucket;
if(!PN_(buffer)(table, 1)) return 0; /* Amortized. */
bucket = table->buckets + (i = PN_(to_bucket_no)(table, hash));/* Closed. */
bucket = table->buckets + (i = PN_(chain_head)(table, hash));/* Closed. */
if(bucket->next != TABLE_NULL) { /* Occupied. */
int in_stack = PN_(to_bucket_no)(table, bucket->hash) != i;
int in_stack = PN_(chain_head)(table, bucket->hash) != i;
PN_(move_to_top)(table, i);
bucket->next = in_stack ? TABLE_END : table->top;
} else { /* Unoccupied. */
@ -487,52 +487,42 @@ static enum table_result PN_(put_key)(struct N_(table) *const table,
return result;
}
/* In no particular order, usually, but deterministic up to topology changes.
@implements `iterator` */
struct PN_(iterator) { struct N_(table) *table; PN_(uint) cur, prev; };
/** Helper to skip the buckets of `it` that are not there.
@return Whether it found another index. */
static int PN_(skip)(struct PN_(iterator) *const it) {
/* In no particular order, usually, but deterministic up to topology changes. */
struct PN_(iterator) { struct N_(table) *table; PN_(uint) i; };
/** @return Before `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; }
/** @return Element at valid non-null `it`. */
static struct PN_(bucket) *PN_(element)(const struct PN_(iterator) *const it)
{ return it->table->buckets + it->i; }
/** @return Whether `it` even has a next. */
static int PN_(next)(struct PN_(iterator) *const it) {
const struct N_(table) *const t = it->table;
const PN_(uint) limit = PN_(capacity)(t);
assert(it && it->table && it->table->buckets);
while(it->cur < limit) {
struct PN_(bucket) *const bucket = t->buckets + it->cur;
if(bucket->next != TABLE_NULL) return 1;
it->cur++;
}
assert(it && it->table);
if(!it->table->buckets) return 0; /* Idle. */
while(++it->i < limit) if(t->buckets[it->i].next != TABLE_NULL) return 1;
return 0;
}
/** @return Before `table`. @implements `begin` */
static struct PN_(iterator) PN_(begin)(struct N_(table) *const table) {
struct PN_(iterator) it; it.table = table, it.cur = 0; it.prev = TABLE_NULL;
return it;
}
/** @return Whether `it` advances and `v`. */
static int PN_(next)(struct PN_(iterator) *const it,
struct PN_(bucket) **const v) {
assert(it);
if(!it->table || !it->table->buckets) return 0;
if(!PN_(skip)(it)) return it->table = 0, it->cur = 0, 0;
if(v) *v = it->table->buckets + it->cur;
it->prev = it->cur, it->cur++;
return 1;
}
/** Removes the entry at `it`. @return Success. */
/** Removes the entry at `it` and possibly corrects `it` so that calling
<fn:<PN>next> will go through the entire list. @return Success. */
static int PN_(remove)(struct PN_(iterator) *const it) {
struct N_(table) *table;
PN_(uint) prev = it->prev;
struct N_(table) *table = it->table;
struct PN_(bucket) *previous = 0, *current;
PN_(uint) prv = TABLE_NULL, crnt;
assert(it);
if(prev == TABLE_NULL) return 0;
table = it->table;
assert(it->table == it->table
&& it->table->buckets && prev < PN_(capacity)(it->table));
/* Egregious code reuse. :[ */
current = it->table->buckets + prev, assert(current->next != TABLE_NULL);
crnt = PN_(to_bucket_no)(it->table, current->hash);
while(crnt != prev) assert(crnt < PN_(capacity)(it->table)),
assert(it && table);
if(!it->table->buckets) return 0;
assert(it->i < PN_(capacity)(it->table));
if(it->i >= PN_(capacity)(it->table)) return 0;
/* This should be possible to simplify with <fn:<PN>prev>?
if(previous = PN_(prev)(table, it->i)) */
/* Egregious code reuse from <fn:<N>table_remove>; because `it` contains
`i` and remove has a `key`, the counting is different. But the rest is the
same? Get the last bucket. */
current = it->table->buckets + it->i, assert(current->next != TABLE_NULL);
crnt = PN_(chain_head)(it->table, current->hash);
while(crnt != it->i) assert(crnt < PN_(capacity)(it->table)),
crnt = (previous = it->table->buckets + (prv = crnt))->next;
if(prv != TABLE_NULL) { /* Open entry. */
previous->next = current->next;
@ -541,11 +531,11 @@ static int PN_(remove)(struct PN_(iterator) *const it) {
struct PN_(bucket) *const second = table->buckets + scnd;
assert(scnd < PN_(capacity)(table));
memcpy(current, second, sizeof *second);
if(crnt < scnd) it->cur = it->prev; /* Iterate new entry. */
crnt = scnd; current = second;
/* Because we replace current with a bucket we haven't seen yet. */
if(crnt < scnd) it->i--;
crnt = scnd, current = second;
}
current->next = TABLE_NULL, table->size--, PN_(shrink_stack)(table, crnt);
it->prev = TABLE_NULL;
return 1;
}
@ -570,34 +560,25 @@ static struct N_(table) N_(table)(void) {
static void N_(table_)(struct N_(table) *const table)
{ if(table) free(table->buckets), *table = N_(table)(); }
/** Loads `table` (can be null) into `it`. @allow */
static struct N_(table_iterator) N_(table_begin)(struct N_(table) *const
table) { struct N_(table_iterator) it; it._ = PN_(begin)(table);
/** Loads a non-null `table` into `it`. @allow */
static struct N_(table_iterator) N_(table_iterator)(struct N_(table) *const
table) { struct N_(table_iterator) it; it._ = PN_(iterator)(table);
return it; }
#ifdef TABLE_VALUE /* <!-- map */
/** Advances `it`. @param[key, value] If non-null, the key or value is filled
with the next element on return true. `value` is a pointer to the actual value
in the map, only there if it is a map.
@return Whether it had a next element. @allow */
static int N_(table_next)(struct N_(table_iterator) *const it,
PN_(key) *key, PN_(value) **value) {
struct PN_(bucket) *bucket;
if(!PN_(next)(&it->_, &bucket)) return 0;
if(key) *key = PN_(bucket_key)(bucket);
if(value) *value = &bucket->value;
return 1;
/** Advances `it`. @return Whether `it` has an element now. @allow */
static int N_(table_next)(struct N_(table_iterator) *const it) {
return PN_(next)(&it->_);
}
#else /* map --><!-- set */
/** Advances `it`, sets `key` on true. */
static int N_(table_next)(struct N_(table_iterator) *const it, PN_(key) *key) {
struct PN_(bucket) *bucket;
if(!PN_(next)(&it->_, &bucket)) return 0;
if(key) *key = PN_(bucket_key)(bucket);
return 1;
}
#endif /* set --> */
/** @return If `it` has an element, returns it's key. */
static PN_(key) N_(table_key)(const struct N_(table_iterator) *const it)
{ return PN_(bucket_key)(it->_.table->buckets + it->_.i); }
#ifdef TABLE_VALUE /* <!-- value */
/** @return If `it` has an element, returns it's value, if `TABLE_VALUE`. */
static PN_(value) *N_(table_value)(const struct N_(table_iterator) *const it)
{ return &it->_.table->buckets[it->_.i].value; }
#endif /* value --> */
/** Removes the entry at `it`. Whereas <fn:<N>table_remove> invalidates the
iterator, this corrects for a signal `it`.
iterator, this corrects `it` so <fn:<N>table_next> is the next entry. To use
the iterator after this, one must move to the next.
@return Success, or there was no entry at the iterator's position, (anymore.)
@allow */
static int N_(table_iterator_remove)(struct N_(table_iterator) *const it)
@ -748,33 +729,33 @@ static enum table_result N_(table_policy)(struct N_(table) *const table,
static int N_(table_remove)(struct N_(table) *const table,
const PN_(key) key) {
struct PN_(bucket) *current;
/* This function must be defined by the user. */
PN_(uint) crnt, prv = TABLE_NULL, nxt, hash = N_(hash)(key);
PN_(uint) c, p = TABLE_NULL, n, hash = N_(hash)(key);
if(!table || !table->size) return 0; assert(table->buckets);
/* Find item and keep track of previous. */
current = table->buckets + (crnt = PN_(to_bucket_no)(table, hash));
if((nxt = current->next) == TABLE_NULL
|| PN_(in_stack_range)(table, crnt)
&& crnt != PN_(to_bucket_no)(table, current->hash)) return 0;
current = table->buckets + (c = PN_(chain_head)(table, hash));
if((n = current->next) == TABLE_NULL /* No entry here. */
|| PN_(in_stack_range)(table, c)
&& c != PN_(chain_head)(table, current->hash)) return 0;
/* Find prev? Why not <fn:<PN>prev>? */
while(hash != current->hash
&& !PN_(equal_buckets)(key, PN_(bucket_key)(current))) {
if(nxt == TABLE_END) return 0;
prv = crnt, current = table->buckets + (crnt = nxt);
assert(crnt < PN_(capacity)(table) && PN_(in_stack_range)(table, crnt)
&& crnt != TABLE_NULL);
nxt = current->next;
if(n == TABLE_END) return 0;
p = c, current = table->buckets + (c = n);
assert(c < PN_(capacity)(table) && PN_(in_stack_range)(table, c)
&& c != TABLE_NULL);
n = current->next;
}
if(prv != TABLE_NULL) { /* Open entry. */
struct PN_(bucket) *previous = table->buckets + prv;
if(p != TABLE_NULL) { /* Open entry. */
struct PN_(bucket) *previous = table->buckets + p;
previous->next = current->next;
} else if(current->next != TABLE_END) { /* Head closed entry and others. */
struct PN_(bucket) *const second
= table->buckets + (crnt = current->next);
= table->buckets + (c = current->next);
assert(current->next < PN_(capacity)(table));
memcpy(current, second, sizeof *second);
current = second;
}
current->next = TABLE_NULL, table->size--, PN_(shrink_stack)(table, crnt);
current->next = TABLE_NULL, table->size--, PN_(shrink_stack)(table, c);
return 1;
}
@ -793,16 +774,17 @@ static void PN_(unused_base_coda)(void);
static void PN_(unused_base)(void) {
PN_(entry) e; PN_(key) k; PN_(value) v;
memset(&e, 0, sizeof e); memset(&k, 0, sizeof k); memset(&v, 0, sizeof v);
N_(table)(); N_(table_)(0); N_(table_begin)(0);
PN_(element)(0);
N_(table)(); N_(table_)(0);
N_(table_iterator)(0); N_(table_key)(0); N_(table_next)(0);
N_(table_buffer)(0, 0); N_(table_clear)(0); N_(table_contains)(0, k);
N_(table_get_or)(0, k, v);
N_(table_update)(0, k, 0); N_(table_policy)(0, k, 0, 0);
N_(table_remove)(0, k); N_(table_iterator_remove)(0);
#ifdef TABLE_VALUE
N_(table_query)(0, k, 0, 0); N_(table_next)(0, 0, 0);
N_(table_assign)(0, k, 0);
N_(table_value)(0); N_(table_query)(0, k, 0, 0); N_(table_assign)(0, k, 0);
#else
N_(table_query)(0, k, 0); N_(table_next)(0, 0); N_(table_try)(0, e);
N_(table_query)(0, k, 0); N_(table_try)(0, e);
#endif
PN_(unused_base_coda)();
}
@ -904,7 +886,7 @@ static void PN_D_(unused, default_coda)(void) { PN_D_(unused, default)(); }
#ifdef TABLE_IS_EQUAL
#undef TABLE_IS_EQUAL
#else
#undef TABLE_INVERSE
#undef TABLE_UNHASH
#endif
#ifdef TABLE_VALUE
#undef TABLE_VALUE

View File

@ -106,7 +106,6 @@ static const char *STR_(to_string)(const PSTR_(box) *const box) {
const size_t ellipsis_len = sizeof ellipsis - 1;
char *const buffer = to_string_buffers[to_string_buffer_i++], *b = buffer;
size_t advance;
PSTR_(element) *v;
struct BOX_(iterator) it;
int is_sep = 0;
/* Minimum size: "(" "XXXXXXXXXXX" "," "…" ")" "\0". */
@ -117,18 +116,18 @@ static const char *STR_(to_string)(const PSTR_(box) *const box) {
{ /* We do not modify `box`, but the compiler doesn't know that. */
PSTR_(box) *promise_box;
memcpy(&promise_box, &box, sizeof box);
it = BOX_(begin)(promise_box);
it = BOX_(iterator)(promise_box);
}
*b++ = left;
while(BOX_(next)(&it, &v)) {
STRCALL_(to_string)(v, (char (*)[12])b);
while(BOX_(next)(&it)) {
STRCALL_(to_string)(BOX_(element)(&it), (char (*)[12])b);
/* Paranoid about '\0'; wastes 1 byte of 12, but otherwise confusing. */
for(advance = 0; *b != '\0' && advance < 11; b++, advance++);
is_sep = 1, *b++ = comma, *b++ = space;
/* Greedy typesetting: enough for "XXXXXXXXXXX" "," "…" ")" "\0". */
if((size_t)(b - buffer) > to_string_buffer_size - 11 - 1
- ellipsis_len - 1 - 1)
{ if(BOX_(next)(&it, 0)) goto ellipsis; else break; }
{ if(BOX_(next)(&it)) goto ellipsis; else break; }
}
if(is_sep) b -= 2;
*b++ = right;

View File

@ -181,6 +181,7 @@ struct PB_(tree) { struct PB_(node) *node; unsigned height; };
![States.](../doc/tree/states.png) */
struct B_(tree);
struct B_(tree) { struct PB_(tree) root; };
/* fixme: height 1-based, fix pointer. */
/* Address of a specific key by node. Height might not be used, but there's too
many structs in this file anyway. */
@ -199,57 +200,48 @@ static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
{ return ref.node ? ref.node->key + ref.idx : 0; }
#endif /* !value --> */
struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) ref; int seen; };
/** @return Before the start of `tree`, (can be null.) @implements `begin` */
static struct PB_(iterator) PB_(begin)(struct B_(tree) *const tree) {
struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) ref; };
/** Iterator for `tree` in empty state. */
static struct PB_(iterator) PB_(iterator)(struct B_(tree) *const tree) {
struct PB_(iterator) it;
it.root = tree ? &tree->root : 0;
it.ref.height = tree ? tree->root.height : 0;
if(tree && tree->root.height != UINT_MAX)
for(it.ref.node = tree->root.node; it.ref.height;
it.ref.node = PB_(as_branch_c)(it.ref.node)->child[0], it.ref.height--);
else it.ref.node = 0;
it.ref.idx = 0;
it.seen = 0;
assert(tree);
it.root = &tree->root;
it.ref.node = 0, it.ref.height = 0, it.ref.idx = 0;
return it;
}
/** @return After the end of `tree`, (can be null.) @implements `end` */
static struct PB_(iterator) PB_(end)(struct B_(tree) *const tree) {
struct PB_(iterator) it;
it.root = tree ? &tree->root : 0;
it.ref.height = tree ? tree->root.height : 0;
if(tree && tree->root.height != UINT_MAX)
for(it.ref.node = tree->root.node; it.ref.height;
it.ref.node = PB_(as_branch)(it.ref.node)->child[it.ref.node->size],
it.ref.height--);
else it.ref.node = 0;
it.ref.idx = it.ref.node ? it.ref.node->size : 0;
it.seen = 0;
return it;
}
/** @return Whether `it` advances, filling `ref`. @implements `next` */
static int PB_(next)(struct PB_(iterator) *const it,
struct PB_(ref) **const ref) {
struct PB_(ref) adv;
assert(it);
if(!it->root || !it->ref.node) return it->seen = 0, 0;
if(!it->root->node || it->root->height == UINT_MAX)
return it->ref.node = 0, 0; /* Concurrent modification? */
adv = it->ref; /* Shorten keystrokes and work with a copy. */
if(!it->seen && adv.idx < adv.node->size) goto successor;
adv.idx++;
if(adv.height && adv.idx > adv.node->size)
return it->ref.node = 0, 0; /* Concurrent modification? */
while(adv.height) adv.height--,
adv.node = PB_(as_branch)(adv.node)->child[adv.idx], adv.idx = 0;
if(adv.idx < adv.node->size) goto successor; /* Likely. */
/* Bulk-loading or concurrent modification? */
if(adv.idx > adv.node->size) return it->ref.node = 0, 0;
{ /* Re-descend; pick the minimum height node that has a next key. */
struct PB_(ref) next;
/** @return Dereference the next (pointing to valid element) `it`. */
static struct PB_(ref) *PB_(element)(struct PB_(iterator) *const it)
{ return &it->ref; }
/** @return Whether `it` pointing to a valid element. */
static int PB_(next)(struct PB_(iterator) *const it) {
struct PB_(ref) next;
assert(it && it->root);
/* Tree empty. */
if(!it->root->node || it->root->height == UINT_MAX) return 0;
/* Iterator empty; tree non-empty; point at first. */
if(!it->ref.node) {
it->ref.height = it->root->height;
for(it->ref.node = it->root->node; it->ref.height;
it->ref.node = PB_(as_branch_c)(it->ref.node)->child[0],
it->ref.height--);
it->ref.idx = 0;
return 1;
}
/* Next is a copy of the next element. Clip. */
next = it->ref, next.idx++;
if(next.height && next.idx > next.node->size) next.idx = next.node->size;
while(next.height) next.node = PB_(as_branch)(next.node)->child[next.idx],
next.idx = 0, next.height--; /* Fall from branch. */
it->ref = next; /* Possibly one beyond bounds. */
if(next.idx >= next.node->size) { /* Maybe re-descend reveals more keys. */
struct PB_(tree) tree = *it->root;
unsigned a0;
const PB_(key) x = adv.node->key[adv.node->size - 1]; /* Target. */
/* Target; this will not work with duplicate keys. */
const PB_(key) x = next.node->key[next.node->size - 1];
assert(next.node->size);
for(next.node = 0; tree.height;
tree.node = PB_(as_branch)(tree.node)->child[a0], tree.height--) {
unsigned a1 = tree.node->size;
@ -262,36 +254,44 @@ static int PB_(next)(struct PB_(iterator) *const it,
if(a0 < tree.node->size) next.node = tree.node,
next.height = tree.height, next.idx = a0;
}
if(!next.node) return it->seen = 0, 0; /* Off right. */
adv = next;
if(!next.node) return it->ref.node = 0, 0; /* Off right. */
} /* Jumped nodes. */
successor:
it->seen = 1;
it->ref = adv;
if(ref) *ref = &it->ref;
it->ref = next;
return 1;
}
/** @return Whether `it` recedes, filling `v`. @implements `next` */
static int PB_(previous)(struct PB_(iterator) *const it,
struct PB_(ref) **const v) {
/** @return Whether `it` is pointing to a valid element. */
static int PB_(previous)(struct PB_(iterator) *const it) {
struct PB_(ref) prd;
assert(it);
if(!it->root || !it->ref.node) return it->seen = 0, 0;
if(!it->root->node || it->root->height == UINT_MAX)
return it->ref.node = 0, 0; /* Concurrent modification? */
prd = it->ref; /* Shorten keystrokes and work with a copy. */
if(prd.idx > prd.node->size) prd.idx = prd.node->size; /* Clip. */
if(!it->seen && prd.idx) { prd.idx--; goto predecessor; }
assert(it && it->root);
/* Tree empty. */
if(!it->root->node || it->root->height == UINT_MAX) return 0;
/* Iterator empty; tree non-empty; point at last. */
if(!it->ref.node) {
it->ref.height = it->root->height;
for(it->ref.node = it->root->node; it->ref.height; it->ref.node
= PB_(as_branch)(it->ref.node)->child[it->ref.node->size],
it->ref.height--);
/* Did you forget <fn:<N>tree_bulk_load_finish>? */
if(!it->ref.node->size) return it->ref.node = 0, 0;
it->ref.idx = it->ref.node->size - 1;
return 1;
}
/* Predecessor? Clip. */
prd = it->ref;
if(prd.height && prd.idx > prd.node->size) prd.idx = prd.node->size;
while(prd.height) prd.height--,
prd.node = PB_(as_branch)(prd.node)->child[prd.idx],
prd.idx = prd.node->size;
if(prd.idx) { prd.idx--; goto predecessor; } /* Likely. */
{ /* Re-descend; pick the minimum height node that has a previous key. */
struct PB_(ref) prev;
if(prd.idx) {
prd.idx--;
} else { /* Maybe re-descend reveals more keys. */
struct PB_(tree) tree = *it->root;
unsigned a0;
const PB_(key) x = prd.node->key[0]; /* Target. */
for(prev.node = 0; tree.height;
for(prd.node = 0; tree.height;
tree.node = PB_(as_branch)(tree.node)->child[a0], tree.height--) {
unsigned a1 = tree.node->size;
a0 = 0;
@ -300,16 +300,12 @@ static int PB_(previous)(struct PB_(iterator) *const it,
if(B_(compare)(x, tree.node->key[m]) > 0) a0 = m + 1;
else a1 = m;
}
if(a0) prev.node = tree.node, prev.height = tree.height,
prev.idx = a0 - 1;
if(a0) prd.node = tree.node, prd.height = tree.height,
prd.idx = a0 - 1;
}
if(!prev.node) return it->seen = 0, 0; /* Off left. */
prd = prev;
if(!prd.node) return it->ref.node = 0, 0; /* Off left. */
} /* Jumped nodes. */
predecessor:
it->seen = 1;
it->ref = prd;
if(v) *v = &it->ref;
return 1;
}
@ -336,13 +332,13 @@ static void PB_(node_ub)(struct PB_(ref) *const hi, const PB_(key) x) {
} while(lo < hi->idx);
}
/** @return A reference to the element first element at or less than `x` in
`tree`, or `node` will be null if the `x` is less than all `tree`. */
static struct PB_(ref) PB_(lookup_left)(const struct PB_(tree) tree,
/** @return A reference to the greatest key at or less than `x` in `tree`, or
the reference will be empty if the `x` is less than all `tree`. */
static struct PB_(ref) PB_(less)(const struct PB_(tree) tree,
const PB_(key) x) {
struct PB_(ref) hi, found;
found.node = 0;
if(!tree.node) return found;
if(!tree.node || tree.height == UINT_MAX) return found;
for(hi.node = tree.node, hi.height = tree.height; ;
hi.node = PB_(as_branch_c)(hi.node)->child[hi.idx], hi.height--) {
if(!(hi.idx = hi.node->size)) continue;
@ -356,30 +352,9 @@ static struct PB_(ref) PB_(lookup_left)(const struct PB_(tree) tree,
}
return found;
}
/** Iterator version of <fn:<PB>lookup_left> of `x` in `tree` that goes
one-off the end. */
static struct PB_(ref) PB_(ref_left)(const struct PB_(tree) tree,
const PB_(key) x) {
struct PB_(ref) hi, found;
found.node = 0;
if(!tree.node) return found;
for(hi.node = tree.node, hi.height = tree.height; ;
hi.node = PB_(as_branch_c)(hi.node)->child[hi.idx], hi.height--) {
if(!(hi.idx = hi.node->size)) continue;
PB_(node_ub)(&hi, x);
if(hi.idx < hi.node->size) {
found = hi;
if(hi.idx && B_(compare)(x, found.node->key[found.idx - 1]) <= 0)
break;
}
if(!hi.height) { if(!found.node) found = hi; break; }
}
return found;
}
/** @return A reference the element at the greatest lower bound of `x` in
`tree`, or if the element doesn't exist, `node` will be null. */
static struct PB_(ref) PB_(lookup_right)(const struct PB_(tree) tree,
/** @return A reference to the smallest key at or more than `x` in `tree`, or
the reference will be empty if the `x` is more than all `tree`. */
static struct PB_(ref) PB_(more)(const struct PB_(tree) tree,
const PB_(key) x) {
struct PB_(ref) lo, found;
found.node = 0;
@ -397,26 +372,6 @@ static struct PB_(ref) PB_(lookup_right)(const struct PB_(tree) tree,
}
return found;
}
/** Iterator version of <fn:<PB>lookup_right> of `x` in `tree` that goes
one-off the end. */
static struct PB_(ref) PB_(ref_right)(const struct PB_(tree) tree,
const PB_(key) x) {
struct PB_(ref) lo, found;
found.node = 0;
if(!tree.node || tree.height == UINT_MAX) return found;
for(lo.node = tree.node, lo.height = tree.height; ;
lo.node = PB_(as_branch_c)(lo.node)->child[lo.idx], lo.height--) {
unsigned hi = lo.node->size; lo.idx = 0;
if(!hi) continue;
PB_(node_lb)(&lo, x);
if(lo.idx < lo.node->size) {
found = lo;
if(B_(compare)(x, lo.node->key[lo.idx]) > 0) break;
}
if(!lo.height) { if(!found.node) found = lo; break; }
}
return found;
}
/** Finds an exact key `x` in non-empty `tree`. */
static struct PB_(ref) PB_(lookup_find)(const struct PB_(tree) tree,
const PB_(key) x) {
@ -563,7 +518,7 @@ static size_t B_(tree_count)(const struct B_(tree) *const tree) {
static int B_(tree_contains)(const struct B_(tree) *const tree,
const PB_(key) x) { return tree && PB_(lookup_find)(tree->root, x).node; }
/* fixme: entry <B>tree_query -- there is no functionality that returns the
key. */
key, which might be important with distinguishable keys. */
/** @return Get the value of `key` in `tree`, or if no key, `default_value`.
The map type is `TREE_VALUE` and the set type is `TREE_KEY`.
@ -578,27 +533,26 @@ static PB_(value) B_(tree_get_or)(const struct B_(tree) *const tree,
/** For example, `tree = { 10 }`, `x = 5 -> default_value`, `x = 10 -> 10`,
`x = 11 -> 10`.
@return Value in `tree` less-then-or-equal to `x` or `default_value` if `x`
is smaller than all in `tree`.
@order \O(\log |`tree`|) @allow */
static PB_(value) B_(tree_left_or)(const struct B_(tree) *const tree,
const PB_(key) x, const PB_(value) default_value) {
@return Key in `tree` less-then-or-equal to `x` or `default_key` if `x` is
smaller than all in `tree`. @order \O(\log |`tree`|) @allow */
static PB_(key) B_(tree_less_or)(const struct B_(tree) *const tree,
const PB_(key) x, const PB_(key) default_key) {
struct PB_(ref) ref;
return tree && (ref = PB_(lookup_left)(tree->root, x)).node ?
(assert(ref.idx < ref.node->size), *PB_(ref_to_valuep)(ref))
: default_value;
return tree && (ref = PB_(less)(tree->root, x)).node ?
(assert(ref.idx < ref.node->size), ref.node->key[ref.idx])
: default_key;
}
/** For example, `tree = { 10 }`, `x = 5 -> 10`, `x = 10 -> 10`,
`x = 11 -> default_value`.
@return Value in `tree` greater-than-or-equal to `x` or `default_value` if `x`
is greater than all in `tree`.
@return Key in `tree` greater-than-or-equal to `x` or `default_key` if `x` is
greater than all in `tree`.
@order \O(\log |`tree`|) @allow */
static PB_(value) B_(tree_right_or)(const struct B_(tree) *const tree,
const PB_(key) x, const PB_(value) default_value) {
static PB_(key) B_(tree_more_or)(const struct B_(tree) *const tree,
const PB_(key) x, const PB_(key) default_key) {
struct PB_(ref) ref;
return tree && (ref = PB_(lookup_right)(tree->root, x)).node
? *PB_(ref_to_valuep)(ref) : default_value;
return tree && (ref = PB_(more)(tree->root, x)).node
? ref.node->key[ref.idx] : default_key;
}
#ifdef TREE_VALUE /* <!-- map */
@ -1526,182 +1480,80 @@ finally:
}
/** Adding, deleting, or changes in the topology of the tree invalidate it. */
/** 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 before the first element of `tree`. Can be null.
@order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_begin)(struct B_(tree) *const tree)
{ struct B_(tree_iterator) it; it._ = PB_(begin)(tree); return it; }
/** @return Cursor after the last element of `tree`. Can be null.
@order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_end)(struct B_(tree) *const tree)
{ struct B_(tree_iterator) it; it._ = PB_(end)(tree); return it; }
/** @return Cursor in `tree` such that <fn:<B>tree_previous> is the greatest
key that is less-than-or-equal to `x`, or, <fn:<B>tree_begin> if `x` is less
than all in `tree`. @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_left_previous)(struct B_(tree) *const
/** @return Cursor at null in valid `tree`. @order \Theta(1) @allow */
static struct B_(tree_iterator) B_(tree_iterator)(struct B_(tree) *const tree)
{ struct B_(tree_iterator) it; it._ = PB_(iterator)(tree); return it; }
/** @return Cursor in `tree` such that <fn:<B>tree_key> is the greatest key
that is less-than-or-equal-to `x`, or if `x` is less than all in `tree`,
<fn:<B>tree_iterator>. @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_less)(struct B_(tree) *const
tree, const PB_(key) x) {
struct B_(tree_iterator) cur;
if(!tree) return cur._.root = 0, cur;
cur._.ref = PB_(ref_left)(tree->root, x);
cur._.root = &tree->root;
cur._.seen = 0;
return cur;
struct B_(tree_iterator) it;
assert(tree);
if(!(it._.root = &tree->root)) return it;
it._.ref = PB_(less)(tree->root, x);
return it;
}
/** @return Cursor in `tree` such that <fn:<B>tree_next> is the least key that
is greater-than-or-equal to `x`, or, <fn:<B>tree_end> if `x` is greater than
all in `tree`. @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_right_next)(struct B_(tree) *const
/** @return Cursor in `tree` such that <fn:<B>tree_more> is the smallest key
that is greater-than-or-equal-to `x`, or, <fn:<B>tree_iterator> if `x` is
greater than all in `tree`. @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_more)(struct B_(tree) *const
tree, const PB_(key) x) {
struct B_(tree_iterator) cur;
if(!tree) return cur._.root = 0, cur;
cur._.ref = PB_(ref_right)(tree->root, x);
cur._.root = &tree->root;
cur._.seen = 0;
return cur;
struct B_(tree_iterator) it;
assert(tree);
if(!(it._.root = &tree->root)) return it;
it._.ref = PB_(more)(tree->root, x);
return it;
}
/** @return Whether valid `it` is pointing to an element. This is the same as
the return value from <fn:<B>tree_next> and <fn:<B>tree_previous> but intended
for <fn:<B>tree_less> and <fn:<B>tree_more> because there's no check for
validity. @allow */
static int B_(tree_has_element)(const struct B_(tree_iterator) *const it) {
return assert(it), it->_.root && it->_.ref.node
&& it->_.ref.idx <= it->_.ref.node->size;
}
/** @return Whether `it` still points at a valid index. @allow */
static int B_(tree_next)(struct B_(tree_iterator) *const it)
{ return assert(it), PB_(next)(&it->_); }
/** @return Whether `it` still points at a valid index. @allow */
static int B_(tree_previous)(struct B_(tree_iterator) *const it)
{ return assert(it), PB_(previous)(&it->_); }
/** @return Extract the key from `it` when it points at a valid index. @allow */
static PB_(key) B_(tree_key)(const struct B_(tree_iterator) *const it)
{ return it->_.ref.node->key[it->_.ref.idx]; }
#ifdef TREE_VALUE /* <!-- map */
/** @return Whether advancing `it` to the next element and filling `k`, (and
`v` if a map, otherwise absent,) if not-null.
@order \O(\log |`tree`|) @allow */
static int B_(tree_next)(struct B_(tree_iterator) *const it,
PB_(key) *const k, PB_(value) **v) {
#else /* map --><!-- set */
static int B_(tree_next)(struct B_(tree_iterator) *const it,
PB_(key) *const k) {
#endif /* set --> */
struct PB_(ref) *r;
if(!PB_(next)(&it->_, &r)) return 0;
if(k) *k = r->node->key[r->idx];
#ifdef TREE_VALUE
if(v) *v = r->node->value + r->idx;
#endif
return 1;
#ifdef TREE_VALUE
}
#else
}
#endif
#ifdef TREE_VALUE /* <!-- map */
/** @return Whether reversing `it` to the previous element and filling `k`,
(and `v` if a map, otherwise absent,) if not-null.
@order \O(\log |`tree`|) @allow */
static int B_(tree_previous)(struct B_(tree_iterator) *const it,
PB_(key) *const k, PB_(value) **v) {
#else /* map --><!-- set */
static int B_(tree_previous)(struct B_(tree_iterator) *const it,
PB_(key) *const k) {
#endif /* set --> */
struct PB_(ref) *r;
if(!PB_(previous)(&it->_, &r)) return 0;
if(k) *k = r->node->key[r->idx];
#ifdef TREE_VALUE
if(v) *v = r->node->value + r->idx;
#endif
return 1;
#ifdef TREE_VALUE
}
#else
}
#endif
/** @return Extract the value from `it` when it points at a valid index, if
`TREE_VALUE`. @allow */
static PB_(value) *B_(tree_value)(const struct B_(tree_iterator) *const it)
{ return it->_.ref.node->value + it->_.ref.idx; }
#endif /* map --> */
#ifdef TREE_VALUE /* <!-- map */
/** Adds `key` and returns `value` to tree in iterator `it`. See
<fn:<B>tree_try>. @return If `it` is not pointing at a valid tree, returns
`TREE_ERROR` and doesn't set `errno`, otherwise the same. */
static enum tree_result B_(tree_iterator_try)(struct B_(tree_iterator) *const
it, const PB_(key) key, PB_(value) **const value) {
#else /* map --><!-- set */
static enum tree_result B_(tree_iterator_try)(struct B_(tree_iterator) *const
it, const PB_(key) key) {
#endif /* set --> */
enum { TREE_NONODE, TREE_ITERATING, TREE_END } where;
PB_(key) anchor;
enum tree_result ret;
memset(&anchor, 0, sizeof anchor); /* Silence warnings. */
if(!it || !it->_.root) return TREE_ERROR; /* No tree. */
if(it->_.ref.node && it->_.root->height != UINT_MAX) {
where = (it->_.ref.idx < it->_.ref.node->size)
? TREE_ITERATING : TREE_END;
} else {
where = TREE_NONODE;
}
if(where == TREE_ITERATING) anchor = it->_.ref.node->key[it->_.ref.idx];
/* Should be already. */
if(where == TREE_NONODE || where == TREE_END) it->_.seen = 0;
#ifdef TREE_VALUE
ret = PB_(update)(it->_.root, key, 0, value);
#else
ret = PB_(update)(it->_.root, key, 0);
#endif
if(ret == TREE_ERROR) return TREE_ERROR;
assert(it->_.root->height != UINT_MAX); /* Can't be empty. */
switch(where) {
case TREE_NONODE: it->_.ref.node = 0; it->_.seen = 0; break;
case TREE_ITERATING:
it->_.ref = PB_(lookup_right)(*it->_.root, anchor); break;
case TREE_END:
assert(it->_.root->node);
it->_.ref.node = it->_.root->node;
it->_.ref.height = it->_.root->height;
it->_.ref.idx = it->_.root->node->size;
while(it->_.ref.height) {
it->_.ref.node
= PB_(as_branch_c)(it->_.ref.node)->child[it->_.ref.idx];
it->_.ref.idx = it->_.ref.node->size;
it->_.ref.height--;
}
it->_.seen = 0;
break;
}
return ret;
#ifdef TREE_VALUE
}
#else
}
#endif
/** Removes the last entry returned by a valid `it`. All other iterators on the
same object are invalidated, but `cur` is now between on the removed node.
@return Success, otherwise `it` is not at a valid element.
@order \Theta(\log |`tree`|) */
static int B_(tree_iterator_remove)(struct B_(tree_iterator) *const it) {
PB_(key) remove;
if(!it || !it->_.seen || !it->_.root || !it->_.ref.node
|| it->_.root->height == UINT_MAX
|| it->_.ref.idx >= it->_.ref.node->size
|| (remove = it->_.ref.node->key[it->_.ref.idx],
!PB_(remove)(it->_.root, remove))) return 0;
/* <fn:<B>tree_begin_at>. */
it->_.ref = PB_(lookup_right)(*it->_.root, remove);
it->_.seen = 0;
return 1;
}
static void PB_(unused_base_coda)(void);
static void PB_(unused_base)(void) {
PB_(key) k; PB_(value) v; memset(&k, 0, sizeof k); memset(&v, 0, sizeof v);
PB_(element)(0);
B_(tree)(); B_(tree_)(0); B_(tree_clear)(0); B_(tree_count)(0);
B_(tree_contains)(0, k); B_(tree_get_or)(0, k, v);
B_(tree_left_or)(0, k, v); B_(tree_right_or)(0, k, v);
B_(tree_less_or)(0, k, k); B_(tree_more_or)(0, k, k);
B_(tree_next)(0); B_(tree_has_element)(0);
#ifdef TREE_VALUE
B_(tree_bulk_add)(0, k, 0); B_(tree_try)(0, k, 0);
B_(tree_assign)(0, k, 0, 0); B_(tree_iterator_try)(0, k, 0);
B_(tree_next)(0, 0, 0); B_(tree_previous)(0, 0, 0);
B_(tree_assign)(0, k, 0, 0); B_(tree_value)(0);
#else
B_(tree_bulk_add)(0, k); B_(tree_try)(0, k);
B_(tree_assign)(0, k, 0); B_(tree_iterator_try)(0, k);
B_(tree_next)(0, 0); B_(tree_previous)(0, 0);
B_(tree_assign)(0, k, 0);
#endif
B_(tree_bulk_finish)(0); B_(tree_remove)(0, k); B_(tree_clone)(0, 0);
B_(tree_begin)(0); B_(tree_end)(0);
B_(tree_left_previous)(0, k); B_(tree_right_next)(0, k);
B_(tree_iterator_remove)(0);
B_(tree_iterator)(0); B_(tree_less)(0, k); B_(tree_more)(0, k);
B_(tree_next)(0); B_(tree_previous)(0); B_(tree_key)(0);
PB_(unused_base_coda)();
}
static void PB_(unused_base_coda)(void) { PB_(unused_base)(); }
@ -1775,35 +1627,10 @@ static PB_(value) B_D_(tree, get)(const struct B_(tree) *const tree,
&& (ref = PB_(lookup_find)(tree->root, key)).node
? *PB_(ref_to_valuep)(ref) : PB_D_(default, value);
}
/** This is functionally identical to <fn:<B>tree_left_or>, but a with a trait
specifying a constant default value.
@return The value associated with `key` in `tree`, (which can be null.) If
no such value exists, the `TREE_DEFAULT` is returned.
@order \O(\log |`tree`|). @allow */
static PB_(value) B_D_(tree, left)(const struct B_(tree) *const tree,
const PB_(key) key) {
struct PB_(ref) ref;
return tree && (ref = PB_(lookup_left)(tree->root, key)).node ?
(assert(ref.idx < ref.node->size), *PB_(ref_to_valuep)(ref))
: PB_D_(default, value);
}
/** This is functionally identical to <fn:<B>tree_right_or>, but a with a trait
specifying a constant default value.
@return The value associated with `key` in `tree`, (which can be null.) If
no such value exists, the `TREE_DEFAULT` is returned.
@order \O(\log |`tree`|). @allow */
static PB_(value) B_D_(tree, right)(const struct B_(tree) *const tree,
const PB_(key) key) {
struct PB_(ref) ref;
return tree && (ref = PB_(lookup_right)(tree->root, key)).node
&& ref.idx < ref.node->size
? *PB_(ref_to_valuep)(ref) : PB_D_(default, value);
}
static void PB_D_(unused, default_coda)(void);
static void PB_D_(unused, default)(void) {
PB_(key) k; memset(&k, 0, sizeof k);
B_D_(tree, get)(0, k); B_D_(tree, left)(0, k); B_D_(tree, right)(0, k);
PB_D_(unused, default_coda)();
B_D_(tree, get)(0, k); PB_D_(unused, default_coda)();
}
static void PB_D_(unused, default_coda)(void) { PB_D_(unused, default)(); }
#undef B_D_

View File

@ -10,7 +10,7 @@ int main(void) {
if(!journal_is_valid(&j)) goto catch;
printf("Journal: %s.\n", journal_to_string(&j));
{
struct journal_iterator it = journal_begin(&j);
struct journal_iterator it = journal_iterator(&j);
union date32 date;
const char *value;
if(!(journal_next(&it, &date, &value))) goto catch;