From 219a3159e8f62ce4624e6f35256fe8a4cf8c0c04 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 20 Aug 2017 12:58:05 +0200 Subject: [PATCH] sort the at_keys array, binary search. --- src/attributes/key.c | 176 +++++++++++++++++++++++++++++--------- src/attributes/key.test.c | 2 +- src/kernel/region.c | 2 +- src/util/gamedata.h | 3 +- 4 files changed, 141 insertions(+), 42 deletions(-) diff --git a/src/attributes/key.c b/src/attributes/key.c index 1bd5cd9b4..ab413755f 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include #include @@ -30,38 +31,94 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. static void a_writekeys(const attrib *a, const void *o, storage *store) { int i, *keys = (int *)a->data.v; - assert(keys[0] < 4096 && keys[0]>0); - WRITE_INT(store, keys[0]); - for (i = 0; i < keys[0]; ++i) { + int n = 0; + if (keys) { + assert(keys[0] < 4096 && keys[0]>0); + n = keys[0]; + } + WRITE_INT(store, n); + for (i = 0; i < n; ++i) { WRITE_INT(store, keys[i * 2 + 1]); WRITE_INT(store, keys[i * 2 + 2]); } } -static int a_readkeys(attrib * a, void *owner, gamedata *data) { - int i, *p = 0; - READ_INT(data->store, &i); - assert(i < 4096 && i>=0); - if (i == 0) { - return AT_READ_FAIL; - } - a->data.v = p = malloc(sizeof(int)*(i*2 + 1)); - *p++ = i; - while (i--) { - READ_INT(data->store, p++); - if (data->version >= KEYVAL_VERSION) { - READ_INT(data->store, p++); +static int keys_lower_bound(int *base, int k, int l, int r) { + int km = k; + int *p = base + 1; + + while (l != r) { + int m = (l + r) / 2; + km = p[m * 2]; + if (km < k) { + if (l == m) l = r; + else l = m; } else { - *p++ = 1; + if (r == m) r = l; + else r = m; } } + return l; +} + +static int a_readkeys(attrib * a, void *owner, gamedata *data) { + int i, n, *keys; + + READ_INT(data->store, &n); + assert(n < 4096 && n >= 0); + if (n == 0) { + return AT_READ_FAIL; + } + + keys = malloc(sizeof(int)*(n * 2 + 1)); + *keys = n; + for (i = 0; i != n; ++i) { + READ_INT(data->store, keys + i * 2 + 1); + if (data->version >= KEYVAL_VERSION) { + READ_INT(data->store, keys + i * 2 + 2); + } + else { + keys[i * 2 + 2] = 1; + } + } + if (data->version < SORTKEYS_VERSION) { + int e = 1; + for (i = 1; i != n; ++i) { + int k = keys[i * 2 + 1]; + int v = keys[i * 2 + 2]; + int l = keys_lower_bound(keys, k, 0, e); + if (l != e) { + int km = keys[l * 2 + 1]; + if (km == k) { + int vm = keys[l * 2 + 2]; + if (v != vm) { + log_error("key %d has values %d and %d", k, v, vm); + } + --e; + } + else { + if (e > l) { + memmove(keys + 2 * l + 3, keys + 2 * l + 1, (e - l) * 2 * sizeof(int)); + } + keys[2 * l + 1] = k; + keys[2 * l + 2] = v; + } + } + ++e; + } + if (e != n) { + keys = realloc(keys, sizeof(int)*(2 * e + 1)); + keys[0] = e; + } + } + a->data.v = keys; return AT_READ_OK; } static int a_readkey(attrib *a, void *owner, struct gamedata *data) { int res = a_readint(a, owner, data); - if (data->version>=KEYVAL_VERSION) { + if (data->version >= KEYVAL_VERSION) { return AT_READ_FAIL; } return (res != AT_READ_FAIL) ? AT_READ_DEPR : res; @@ -101,9 +158,46 @@ attrib_type at_key = { a_upgradekeys }; +static int* keys_get(int *base, int i) +{ + int n = base[0]; + assert(i >= 0 && i < n); + return base + 1 + i * 2; +} + +static int *keys_update(int *base, int key, int val) +{ + int *kv; + int n = base[0]; + int l = keys_lower_bound(base, key, 0, n); + if (l < n) { + kv = keys_get(base, l); + if (kv[0] == key) { + kv[1] = val; + } + else { + assert(kv[0] > key); + base = realloc(base, (n * 2 + 3) * sizeof(int)); + kv = keys_get(base, l); + base[0] = n + 1; + memmove(kv + 2, kv, 2 * sizeof(int) * (n - l)); + kv[0] = key; + kv[1] = val; + } + } + else { + base = realloc(base, (n * 2 + 3) * sizeof(int)); + base[0] = n + 1; + kv = keys_get(base, l); + kv[0] = key; + kv[1] = val; + } + return base; +} + void key_set(attrib ** alist, int key, int val) { - int *keys, n = 0; + int *keys; attrib *a; assert(key != 0); a = a_find(*alist, &at_keys); @@ -111,16 +205,16 @@ void key_set(attrib ** alist, int key, int val) a = a_add(alist, a_new(&at_keys)); } keys = (int *)a->data.v; - if (keys) { - n = keys[0]; + if (!keys) { + a->data.v = keys = malloc(3 * sizeof(int)); + keys[0] = 1; + keys[1] = key; + keys[2] = val; + } + else { + a->data.v = keys = keys_update(keys, key, val); + assert(keys[0] < 4096 && keys[0] >= 0); } - /* TODO: too many allocations, unsorted array */ - keys = realloc(keys, sizeof(int) *(2 * n + 3)); - keys[0] = n + 1; - assert(keys[0] < 4096 && keys[0]>=0); - keys[2 * n + 1] = key; - keys[2 * n + 2] = val; - a->data.v = keys; } void key_unset(attrib ** alist, int key) @@ -129,17 +223,19 @@ void key_unset(attrib ** alist, int key) assert(key != 0); a = a_find(*alist, &at_keys); if (a) { - int i, *keys = (int *)a->data.v; + int *keys = (int *)a->data.v; if (keys) { int n = keys[0]; - assert(keys[0] < 4096 && keys[0]>0); - for (i = 0; i != n; ++i) { - if (keys[2 * i + 1] == key) { - memmove(keys + 2 * i + 1, keys + 2 * n - 1, 2 * sizeof(int)); + int l = keys_lower_bound(keys, key, 0, n); + if (l < n) { + int *kv = keys_get(keys, l); + if (kv[0] == key) { + memmove(kv, kv + 2, (n - l - 1) * 2 * sizeof(int)); + // TODO: realloc to smaller size? keys[0]--; - break; } } + assert(keys[0] < 4096 && keys[0]>0); } } } @@ -149,12 +245,14 @@ int key_get(attrib *alist, int key) { assert(key != 0); a = a_find(alist, &at_keys); if (a) { - int i, *keys = (int *)a->data.v; + int *keys = (int *)a->data.v; if (keys) { - /* TODO: binary search this! */ - for (i = 0; i != keys[0]; ++i) { - if (keys[i*2+1] == key) { - return keys[i * 2 + 2]; + int n = keys[0]; + int l = keys_lower_bound(keys, key, 0, n); + if (l < n) { + int * kv = keys_get(keys, l); + if (kv[0] == key) { + return kv[1]; } } } diff --git a/src/attributes/key.test.c b/src/attributes/key.test.c index b816e1d86..79a7bde1d 100644 --- a/src/attributes/key.test.c +++ b/src/attributes/key.test.c @@ -32,8 +32,8 @@ static void test_upgrade_key(CuTest *tc) { attrib *alist = 0; key_set_orig(&alist, 40); key_set_orig(&alist, 41); - key_set_orig(&alist, 42); key_set_orig(&alist, 43); + key_set_orig(&alist, 42); key_set_orig(&alist, 44); CuAssertPtrNotNull(tc, alist->type->upgrade); alist->type->upgrade(&alist, alist); diff --git a/src/kernel/region.c b/src/kernel/region.c index b582c406e..0eabc298c 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -584,7 +584,7 @@ int rroad(const region * r, direction_t d) bool r_isforest(const region * r) { if (fval(r->terrain, FOREST_REGION)) { - /* needs to be covered with at leas 48% trees */ + /* needs to be covered with at least 48% trees */ int mincover = (int)(r->terrain->size * 0.48); int trees = rtrees(r, 2) + rtrees(r, 1); return (trees * TREESIZE >= mincover); diff --git a/src/util/gamedata.h b/src/util/gamedata.h index f4983a981..97f8416b3 100644 --- a/src/util/gamedata.h +++ b/src/util/gamedata.h @@ -35,10 +35,11 @@ #define KEYVAL_VERSION 355 /* at_keys has values */ #define NOLANDITEM_VERSION 356 /* land_region has no items */ #define NORCSPELL_VERSION 357 /* data contains no RC_SPELL units */ +#define SORTKEYS_VERSION 358 /* at_keys is sorted */ /* unfinished: */ #define CRYPT_VERSION 400 /* passwords are encrypted */ -#define RELEASE_VERSION NORCSPELL_VERSION /* current datafile */ +#define RELEASE_VERSION SORTKEYS_VERSION /* current datafile */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */