diff --git a/src/gamecode/laws_test.c b/src/gamecode/laws_test.c index fce2fa060..0a2bb952c 100644 --- a/src/gamecode/laws_test.c +++ b/src/gamecode/laws_test.c @@ -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 #include #include "laws.h" diff --git a/src/gamecode/market_test.c b/src/gamecode/market_test.c index 1e7eefb8e..985538368 100644 --- a/src/gamecode/market_test.c +++ b/src/gamecode/market_test.c @@ -18,7 +18,7 @@ #include -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); - terraform_region(r, terrain); + 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; } diff --git a/src/kernel/item.c b/src/kernel/item.c index 387112f71..25c32fe14 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -41,6 +41,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -55,13 +56,11 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -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(lenrtype); } } @@ -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(lennext) { - 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); + } + } + } while (m==CB_BATCHSIZE); + inames = in; } - if (findtoken(in->names, name, &var) == E_TOK_NOMATCH) - return NULL; - return (const item_type *)var.v; + + 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 +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; +} + +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); + return 0; +} + void test_clear_resources(void) { - int i; + memset((void *)olditemtype, 0, sizeof(olditemtype)); + memset((void *)oldresourcetype, 0, sizeof(oldresourcetype)); + memset((void *)oldpotiontype, 0, sizeof(oldpotiontype)); - for (i=0;i!=IMAXHASH;++i) { - item_type * itype = itemtypes[i]; - if (itype) { - itemtypes[i] = 0; - free(itype->construction); - free(itype); - } - } + 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); - while (resourcetypes) { - resource_type * rtype = resourcetypes; - resourcetypes = rtype->next; - 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(); + r_hp = r_silver = r_aura = r_permaura = r_unit = 0; + i_silver = 0; } #endif diff --git a/src/kernel/item.h b/src/kernel/item.h index b08ff2b35..7f70f57c8 100644 --- a/src/kernel/item.h +++ b/src/kernel/item.h @@ -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; diff --git a/src/kernel/item_test.c b/src/kernel/item_test.c index 90a668386..4ce7d8bf9 100644 --- a/src/kernel/item_test.c +++ b/src/kernel/item_test.c @@ -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) diff --git a/src/tests.c b/src/tests.c index 6b3ac59d1..3da1d8096 100644 --- a/src/tests.c +++ b/src/tests.c @@ -4,6 +4,7 @@ #include #include "tests.h" +#include "tests_test.c" #include #include #include @@ -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); diff --git a/src/tests_test.c b/src/tests_test.c new file mode 100644 index 000000000..65c678e14 --- /dev/null +++ b/src/tests_test.c @@ -0,0 +1,26 @@ +#include +#include + +#include + +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; +} diff --git a/src/util/critbit.c b/src/util/critbit.c index a4e55950b..cd902d6ee 100644 --- a/src/util/critbit.c +++ b/src/util/critbit.c @@ -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,34 +227,45 @@ 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_find_prefix_i(top, key, keylen, results, numresults, &offset, 0); + return cb_foreach_i(top, key, keylen, match_cb, data); } return 0; } diff --git a/src/util/critbit.h b/src/util/critbit.h index 9240c582d..0d9c11c89 100644 --- a/src/util/critbit.h +++ b/src/util/critbit.h @@ -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) \