soucemap pair -> size_t doesn't make sense; I think. Should be line64 -> size_t?
This commit is contained in:
parent
fbabdf9810
commit
b69e40bc44
73
src/array.h
73
src/array.h
@ -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);
|
||||
|
@ -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 --> */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
220
src/table.h
220
src/table.h
@ -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
|
||||
|
@ -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;
|
||||
|
461
src/tree.h
461
src/tree.h
@ -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. */
|
||||
/** @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_
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user