rt_find and it_find use new improved critbit-tree

critbit update
testing the test suite
This commit is contained in:
Enno Rehling 2012-05-19 11:26:46 -07:00
parent f47f83ceca
commit 7f7c883583
9 changed files with 239 additions and 184 deletions

View File

@ -1,6 +1,3 @@
/* this file should only be included by laws.c, never compiled on its own
* (it does not even include all the headers needed to do so).
**/
#include <platform.h>
#include <kernel/types.h>
#include "laws.h"

View File

@ -18,7 +18,7 @@
#include <stdlib.h>
static void market_curse(CuTest * tc)
static void test_market_curse(CuTest * tc)
{
region *r;
building *b;
@ -26,16 +26,21 @@ static void market_curse(CuTest * tc)
faction *f;
int x, y;
const char *names[4] = { "herb", "herbs", "balm", "balms" };
terrain_type *terrain;
resource_type *hres = new_resourcetype(names, 0, RTF_ITEM | RTF_POOLED);
item_type *htype = new_itemtype(hres, ITF_HERB, 0, 0);
resource_type *lres = new_resourcetype(names + 2, 0, RTF_ITEM | RTF_POOLED);
item_type *ltype = new_itemtype(lres, ITF_NONE, 0, 0);
luxury_type *lux = new_luxurytype(ltype, 0);
const terrain_type *terrain;
resource_type *hres, *lres;
item_type *htype, *ltype;
luxury_type *lux;
building_type *btype;
race *rc = rc_add(rc_new("human"));
free_gamedata();
test_cleanup();
test_create_world();
hres = new_resourcetype(names, 0, RTF_ITEM | RTF_POOLED);
htype = new_itemtype(hres, ITF_HERB, 0, 0);
lres = new_resourcetype(names + 2, 0, RTF_ITEM | RTF_POOLED);
ltype = new_itemtype(lres, ITF_NONE, 0, 0);
lux = new_luxurytype(ltype, 0);
set_param(&global.parameters, "rules.region_owners", "1");
@ -43,12 +48,16 @@ static void market_curse(CuTest * tc)
btype->_name = "market";
bt_register(btype);
terrain = test_create_terrain("plain", LAND_REGION | WALK_INTO);
terrain = get_terrain("plain");
for (x = 0; x != 3; ++x) {
for (y = 0; y != 3; ++y) {
r = new_region(x, y, NULL, 0);
r = findregion(x, y);
if (!r) {
r = test_create_region(x, y, terrain);
} else {
terraform_region(r, terrain);
}
rsetpeasants(r, 5000);
r_setdemand(r, lux, 0);
rsetherbtype(r, htype);
@ -59,8 +68,8 @@ static void market_curse(CuTest * tc)
b->flags |= BLD_WORKING;
b->size = b->type->maxsize;
f = addfaction("nobody@eressea.de", NULL, rc, default_locale, 0);
u = create_unit(r, f, 1, f->race, 0, 0, 0);
f = test_create_faction(0);
u = test_create_unit(f, r);
u_set_building(u, b);
do_markets();
@ -72,6 +81,6 @@ static void market_curse(CuTest * tc)
CuSuite *get_market_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, market_curse);
SUITE_ADD_TEST(suite, test_market_curse);
return suite;
}

View File

@ -41,6 +41,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include <util/attrib.h>
#include <util/base36.h>
#include <util/critbit.h>
#include <util/event.h>
#include <util/functions.h>
#include <util/goodies.h>
@ -55,13 +56,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <stdlib.h>
#include <string.h>
resource_type *resourcetypes;
static critbit_tree cb_resources;
static critbit_tree cb_items;
luxury_type *luxurytypes;
potion_type *potiontypes;
#define IMAXHASH 127
static item_type *itemtypes[IMAXHASH];
static int res_changeaura(unit * u, const resource_type * rtype, int delta)
{
assert(rtype != NULL);
@ -173,14 +172,13 @@ resource_type *new_resourcetype(const char **names, const char **appearances,
void it_register(item_type * itype)
{
int hash = hashstring(itype->rtype->_name[0]);
int key = hash % IMAXHASH;
item_type **p_itype = &itemtypes[key];
while (*p_itype && *p_itype != itype)
p_itype = &(*p_itype)->next;
if (*p_itype == NULL) {
itype->rtype->itype = itype;
*p_itype = itype;
char buffer[64];
const char * name = itype->rtype->_name[0];
size_t len = strlen(name);
assert(len<sizeof(buffer)-sizeof(itype));
cb_new_kv(name, &itype, sizeof(itype), buffer);
if (cb_insert(&cb_items, buffer, len+1+sizeof(itype))) {
rt_register(itype->rtype);
}
}
@ -294,16 +292,13 @@ potion_type *new_potiontype(item_type * itype, int level)
void rt_register(resource_type * rtype)
{
resource_type **prtype = &resourcetypes;
char buffer[64];
const char * name = rtype->_name[0];
size_t len = strlen(name);
if (!rtype->hashkey) {
rtype->hashkey = hashstring(rtype->_name[0]);
}
while (*prtype && *prtype != rtype)
prtype = &(*prtype)->next;
if (*prtype == NULL) {
*prtype = rtype;
}
assert(len<sizeof(buffer)-sizeof(rtype));
cb_new_kv(name, &rtype, sizeof(rtype), buffer);
cb_insert(&cb_resources, buffer, len+1+sizeof(rtype));
}
const resource_type *item2resource(const item_type * itype)
@ -347,17 +342,13 @@ const potion_type *resource2potion(const resource_type * rtype)
resource_type *rt_find(const char *name)
{
unsigned int hash = hashstring(name);
resource_type *rtype;
const void * matches;
resource_type *result = 0;
for (rtype = resourcetypes; rtype; rtype = rtype->next) {
if (rtype->hashkey == hash && !strcmp(rtype->_name[0], name))
break;
if (cb_find_prefix(&cb_resources, name, strlen(name)+1, &matches, 1, 0)) {
cb_get_kv(matches, &result, sizeof(result));
}
if (!rtype) {
log_warning("rt_find: unknown resource '%s'\n", name);
}
return rtype;
return result;
}
static const char *it_aliases[][2] = {
@ -387,23 +378,13 @@ static const char *it_alias(const char *zname)
item_type *it_find(const char *zname)
{
const char *name = it_alias(zname);
unsigned int hash = hashstring(name);
item_type *itype;
unsigned int key = hash % IMAXHASH;
const void * matches;
item_type *result = 0;
for (itype = itemtypes[key]; itype; itype = itype->next) {
if (itype->rtype->hashkey == hash
&& strcmp(itype->rtype->_name[0], name) == 0) {
break;
if (cb_find_prefix(&cb_items, name, strlen(name)+1, &matches, 1, 0)) {
cb_get_kv(matches, &result, sizeof(result));
}
}
if (itype == NULL) {
for (itype = itemtypes[key]; itype; itype = itype->next) {
if (strcmp(itype->rtype->_name[1], name) == 0)
break;
}
}
return itype;
return result;
}
item **i_find(item ** i, const item_type * it)
@ -1066,10 +1047,11 @@ int change_money(unit * u, int v)
}
static local_names *rnames;
#define CB_BATCHSIZE 16
const resource_type *findresourcetype(const char *name,
const struct locale *lang)
{
const void * matches[CB_BATCHSIZE];
local_names *rn = rnames;
variant token;
@ -1079,21 +1061,30 @@ const resource_type *findresourcetype(const char *name,
rn = rn->next;
}
if (!rn) {
const resource_type *rtl = resourcetypes;
rn = calloc(sizeof(local_names), 1);
int m, offset = 0;
rn = (local_names *)calloc(1, sizeof(local_names));
rn->next = rnames;
rn->lang = lang;
while (rtl) {
token.v = (void *)rtl;
addtoken(&rn->names, locale_string(lang, rtl->_name[0]), token);
addtoken(&rn->names, locale_string(lang, rtl->_name[1]), token);
rtl = rtl->next;
do {
m = cb_find_prefix(&cb_resources, "", 1, matches, CB_BATCHSIZE, offset);
if (m) {
int i;
offset += m;
for (i=0;i!=m;++i) {
resource_type *rtype;
cb_get_kv(matches[i], &rtype, sizeof(rtype));
token.v = (void *)rtype;
addtoken(&rn->names, locale_string(lang, rtype->_name[0]), token);
addtoken(&rn->names, locale_string(lang, rtype->_name[1]), token);
}
}
} while (m==CB_BATCHSIZE);
rnames = rn;
}
if (findtoken(rn->names, name, &token) == E_TOK_NOMATCH)
return NULL;
if (findtoken(rn->names, name, &token) == E_TOK_NOMATCH) {
return 0;
}
return (const resource_type *)token.v;
}
@ -1103,64 +1094,43 @@ attrib_type at_showitem = {
static local_names *inames;
void init_itemnames(void)
{
int i;
for (i = 0; localenames[i]; ++i) {
const struct locale *lang = find_locale(localenames[i]);
boolean exist = false;
local_names *in = inames;
while (in != NULL) {
if (in->lang == lang) {
exist = true;
break;
}
in = in->next;
}
if (in == NULL)
in = calloc(sizeof(local_names), 1);
in->next = inames;
in->lang = lang;
if (!exist) {
int key;
for (key = 0; key != IMAXHASH; ++key) {
const item_type *itl;
for (itl = itemtypes[key]; itl; itl = itl->next) {
variant var;
const char *iname = locale_string(lang, itl->rtype->_name[0]);
if (findtoken(in->names, iname, &var) == E_TOK_NOMATCH
|| var.v != itl) {
var.v = (void *)itl;
addtoken(&in->names, iname, var);
addtoken(&in->names, locale_string(lang, itl->rtype->_name[1]),
var);
}
}
}
}
inames = in;
}
}
const item_type *finditemtype(const char *name, const struct locale *lang)
{
const void * matches[CB_BATCHSIZE];
local_names *in = inames;
variant var;
variant token;
while (in != NULL) {
while (in) {
if (in->lang == lang)
break;
in = in->next;
}
if (in == NULL) {
init_itemnames();
for (in = inames; in->lang != lang; in = in->next) ;
if (!in) {
int m, offset = 0;
in = (local_names *)calloc(1, sizeof(local_names));
in->next = inames;
in->lang = lang;
do {
m = cb_find_prefix(&cb_items, "", 1, matches, CB_BATCHSIZE, offset);
if (m) {
int i;
offset += m;
for (i=0;i!=m;++i) {
item_type *itype;
cb_get_kv(matches[i], &itype, sizeof(itype));
token.v = (void *)itype;
addtoken(&in->names, locale_string(lang, itype->rtype->_name[0]), token);
addtoken(&in->names, locale_string(lang, itype->rtype->_name[1]), token);
}
if (findtoken(in->names, name, &var) == E_TOK_NOMATCH)
return NULL;
return (const item_type *)var.v;
}
} while (m==CB_BATCHSIZE);
inames = in;
}
if (findtoken(in->names, name, &token) == E_TOK_NOMATCH) {
return 0;
}
return (const item_type *)token.v;
}
static void init_resourcelimit(attrib * a)
@ -1197,31 +1167,38 @@ static item *default_spoil(const struct race *rc, int size)
}
#ifndef DISABLE_TESTS
void test_clear_resources(void)
{
int i;
for (i=0;i!=IMAXHASH;++i) {
item_type * itype = itemtypes[i];
if (itype) {
itemtypes[i] = 0;
int free_itype_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
item_type *itype;
cb_get_kv(match, &itype, sizeof(itype));
free(itype->construction);
free(itype);
}
}
return 0;
}
while (resourcetypes) {
resource_type * rtype = resourcetypes;
resourcetypes = rtype->next;
int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
resource_type *rtype;
cb_get_kv(match, &rtype, sizeof(rtype));
free(rtype->_name[0]);
free(rtype->_name[1]);
free(rtype->_appearance[0]);
free(rtype->_appearance[1]);
free(rtype);
}
resourcetypes = 0;
r_hp = 0;
init_resources();
return 0;
}
void test_clear_resources(void)
{
memset((void *)olditemtype, 0, sizeof(olditemtype));
memset((void *)oldresourcetype, 0, sizeof(oldresourcetype));
memset((void *)oldpotiontype, 0, sizeof(oldpotiontype));
cb_foreach(&cb_items, "", 0, free_itype_cb, 0);
cb_clear(&cb_items);
cb_foreach(&cb_resources, "", 0, free_rtype_cb, 0);
cb_clear(&cb_resources);
r_hp = r_silver = r_aura = r_permaura = r_unit = 0;
i_silver = 0;
}
#endif

View File

@ -69,15 +69,13 @@ extern "C" {
rtype_name name;
/* --- pointers --- */
struct attrib *attribs;
struct resource_type *next;
unsigned int hashkey;
struct item_type *itype;
struct potion_type *ptype;
struct luxury_type *ltype;
struct weapon_type *wtype;
struct armor_type *atype;
} resource_type;
extern resource_type *resourcetypes;
extern const char *resourcename(const resource_type * rtype, int flags);
extern const resource_type *findresourcetype(const char *name,
const struct locale *lang);
@ -142,12 +140,9 @@ extern "C" {
#if SCORE_MODULE
int score;
#endif
struct item_type *next;
} item_type;
extern const item_type *finditemtype(const char *name,
const struct locale *lang);
extern void init_itemnames(void);
extern const item_type *finditemtype(const char *name, const struct locale *lang);
typedef struct luxury_type {
struct luxury_type *next;

View File

@ -7,19 +7,19 @@
void test_resource_type(CuTest * tc)
{
resource_type *rtype, *rherp;
resource_type *rtype;
const char *names[2] = { 0 , 0 };
CuAssertPtrEquals(tc, 0, rt_find("herpderp"));
names[0] = "herpderp";
rtype = new_resourcetype(names, NULL, RTF_NONE);
new_resourcetype(names, NULL, RTF_NONE);
names[0] = "herp";
rherp = new_resourcetype(names, NULL, RTF_NONE);
names[0] = "herpes";
rtype = new_resourcetype(names, NULL, RTF_NONE);
names[0] = "herpes";
new_resourcetype(names, NULL, RTF_NONE);
CuAssertPtrEquals(tc, rherp, rt_find("herp"));
CuAssertPtrEquals(tc, rtype, rt_find("herp"));
}
CuSuite *get_item_suite(void)

View File

@ -4,6 +4,7 @@
#include <platform.h>
#include "tests.h"
#include "tests_test.c"
#include <util/base36_test.c>
#include <util/functions_test.c>
#include <util/quicklist_test.c>
@ -38,22 +39,26 @@ int RunAllTests(void)
int flags = log_flags;
log_flags = LOG_FLUSH | LOG_CPERROR;
init_resources();
/* self-test */
CuSuiteAddSuite(suite, get_tests_suite());
/* util */
CuSuiteAddSuite(suite, get_base36_suite());
CuSuiteAddSuite(suite, get_quicklist_suite());
CuSuiteAddSuite(suite, get_functions_suite());
CuSuiteAddSuite(suite, get_umlaut_suite());
/* kernel */
CuSuiteAddSuite(suite, get_curse_suite());
CuSuiteAddSuite(suite, get_market_suite());
CuSuiteAddSuite(suite, get_item_suite());
CuSuiteAddSuite(suite, get_move_suite());
CuSuiteAddSuite(suite, get_reports_suite());
CuSuiteAddSuite(suite, get_ship_suite());
CuSuiteAddSuite(suite, get_building_suite());
CuSuiteAddSuite(suite, get_spell_suite());
CuSuiteAddSuite(suite, get_laws_suite());
CuSuiteAddSuite(suite, get_battle_suite());
/* gamecode */
CuSuiteAddSuite(suite, get_market_suite());
CuSuiteAddSuite(suite, get_laws_suite());
CuSuiteRun(suite);
CuSuiteSummary(suite, output);
@ -157,9 +162,12 @@ void test_create_world(void)
building_type *btype;
ship_type *stype;
item_type * itype;
const char * names[2] = { "horse", "horse_p" };
const char * horses[2] = { "horse", "horse_p" };
itype = test_create_itemtype(names);
init_resources();
assert(!olditemtype[I_HORSE]);
itype = test_create_itemtype(horses);
olditemtype[I_HORSE] = itype;
t_plain = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION);

26
src/tests_test.c Normal file
View File

@ -0,0 +1,26 @@
#include <kernel/item.h>
#include <kernel/region.h>
#include <cutest/CuTest.h>
static void test_recreate_world(CuTest * tc)
{
test_cleanup();
CuAssertPtrEquals(tc, 0, it_find("money"));
CuAssertPtrEquals(tc, 0, it_find("horse"));
test_create_world();
CuAssertPtrNotNull(tc, it_find("money"));
CuAssertPtrNotNull(tc, it_find("horse"));
CuAssertPtrNotNull(tc, findregion(0, 0));
test_cleanup();
CuAssertPtrEquals(tc, 0, it_find("money"));
CuAssertPtrEquals(tc, 0, it_find("horse"));
CuAssertPtrEquals(tc, 0, findregion(0, 0));
}
CuSuite *get_tests_suite(void)
{
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_recreate_world);
return suite;
}

View File

@ -85,6 +85,7 @@ void cb_clear(critbit_tree * cb)
{
if (cb->root) {
cb_free_node(cb->root);
cb->root = 0;
}
}
@ -166,6 +167,36 @@ int cb_insert(critbit_tree * cb, const void * key, size_t keylen)
}
}
static void * cb_find_top_i(critbit_tree * cb, const void * key, size_t keylen)
{
void *ptr, *top = 0;
assert(key);
if (!cb->root) {
return 0;
}
for (ptr=cb->root, top=cb->root;;) {
void * last = ptr;
if (decode_pointer(&ptr)==INTERNAL_NODE) {
struct critbit_node * node = (struct critbit_node *)ptr;
int branch;
if (keylen<=node->byte) {
break;
} else {
unsigned char * bytes = (unsigned char *)key;
top = last;
branch = (1+((bytes[node->byte]|node->mask)&0xFF))>>8;
ptr = node->child[branch];
}
} else {
/* we reached an external node before exhausting the key length */
top = last;
break;
}
}
return top;
}
static int cb_find_prefix_i(void * ptr, const void * key, size_t keylen, const void ** results, int numresults, int * offset, int next)
{
assert(next<=numresults);
@ -196,35 +227,46 @@ static int cb_find_prefix_i(void * ptr, const void * key, size_t keylen, const v
int cb_find_prefix(critbit_tree * cb, const void * key, size_t keylen, const void ** results, int numresults, int offset)
{
void *ptr, *top = 0;
assert(key);
if (!cb->root || !numresults) {
return 0;
}
for (ptr=cb->root, top=cb->root;;) {
void * last = ptr;
if (decode_pointer(&ptr)==INTERNAL_NODE) {
struct critbit_node * node = (struct critbit_node *)ptr;
int branch;
if (keylen<=node->byte) {
break;
} else {
unsigned char * bytes = (unsigned char *)key;
top = last;
branch = (1+((bytes[node->byte]|node->mask)&0xFF))>>8;
ptr = node->child[branch];
}
} else {
/* we reached an external node before exhausting the key length */
top = last;
break;
}
}
if (numresults>0) {
void *top = cb_find_top_i(cb, key, keylen);
if (top) {
/* recursively add all children except the ones from [0-offset) of top to the results */
return cb_find_prefix_i(top, key, keylen, results, numresults, &offset, 0);
}
}
return 0;
}
static int cb_foreach_i(void * ptr, const void * key, size_t keylen, int (*match_cb)(const void * match, const void * key, size_t keylen, void *), void *data)
{
int result = 0;
if (decode_pointer(&ptr)==INTERNAL_NODE) {
struct critbit_node * node = (struct critbit_node *)ptr;
result = cb_foreach_i(node->child[0], key, keylen, match_cb, data);
if (!result) {
result = cb_foreach_i(node->child[1], key, keylen, match_cb, data);
}
} else {
/* reached an external node */
void * match;
size_t len;
from_external_node(ptr, &match, &len);
if (len>=keylen && memcmp(key, match, keylen)==0) {
return match_cb(match, key, keylen, data);
}
}
return result;
}
int cb_foreach(critbit_tree * cb, const void * key, size_t keylen, int (*match_cb)(const void * match, const void * key, size_t keylen, void *), void *data)
{
void *top = cb_find_top_i(cb, key, keylen);
if (top) {
/* recursively add all children except the ones from [0-offset) of top to the results */
return cb_foreach_i(top, key, keylen, match_cb, data);
}
return 0;
}

View File

@ -35,6 +35,7 @@ int cb_insert(critbit_tree * cb, const void * key, size_t keylen);
const void * cb_find(critbit_tree * cb, const void * key, size_t keylen);
int cb_erase(critbit_tree * cb, const void * key, size_t keylen);
int cb_find_prefix(critbit_tree * cb, const void * key, size_t keylen, const void ** results, int numresults, int offset);
int cb_foreach(critbit_tree * cb, const void * key, size_t keylen, int (*match_cb)(const void * match, const void * key, size_t keylen, void *), void *data);
void cb_clear(critbit_tree * cb);
#define cb_insert_str(cb, key) \