--- trunk/src/osm.c 2009/01/20 16:50:14 41 +++ trunk/src/osm.c 2009/01/21 20:01:18 42 @@ -17,10 +17,11 @@ * along with OSM2Go. If not, see . */ -/* xml parsing has a performance issue */ +/* these defines select one of three possible xml parsers */ +/* this is in fact selected depending on the plattform in the Makefile */ // #define OSM_DOM_PARSER // #define OSM_STREAM_PARSER -#define OSM_QND_XML_PARSER +// #define OSM_QND_XML_PARSER #include #include @@ -370,6 +371,15 @@ xmlFree(prop); } + /* append node to end of hash table if present */ + if(osm->node_hash) { + hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)]; + while(*item) item = &(*item)->next; + + *item = g_new0(hash_item_t, 1); + (*item)->data.node = node; + } + /* scan for tags and attach a list of tags */ tag_t **tag = &node->tag; for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) { @@ -481,14 +491,9 @@ node_chain_t *node_chain = g_new0(node_chain_t, 1); /* search matching node */ - node_chain->node = osm->node; - while(node_chain->node && node_chain->node->id != id) - node_chain->node = node_chain->node->next; - + node_chain->node = osm_get_node_by_id(osm, id); if(!node_chain->node) printf("Node id %lu not found\n", id); - - if(node_chain->node) - node_chain->node->ways++; + else node_chain->node->ways++; xmlFree(prop); @@ -527,6 +532,15 @@ xmlFree(prop); } + /* append way to end of hash table if present */ + if(osm->way_hash) { + hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)]; + while(*item) item = &(*item)->next; + + *item = g_new0(hash_item_t, 1); + (*item)->data.way = way; + } + /* scan for tags/nodes and attach their lists */ tag_t **tag = &way->tag; node_chain_t **node_chain = &way->node_chain; @@ -652,10 +666,7 @@ case WAY: /* search matching way */ - member->way = osm->way; - while(member->way && member->way->id != id) - member->way = member->way->next; - + member->way = osm_get_way_by_id(osm, id); if(!member->way) { member->type = WAY_ID; member->id = id; @@ -664,10 +675,7 @@ case NODE: /* search matching node */ - member->node = osm->node; - while(member->node && member->node->id != id) - member->node = member->node->next; - + member->node = osm_get_node_by_id(osm, id); if(!member->node) { member->type = NODE_ID; member->id = id; @@ -676,10 +684,7 @@ case RELATION: /* search matching relation */ - member->relation = osm->relation; - while(member->relation && member->relation->id != id) - member->relation = member->relation->next; - + member->relation = osm_get_relation_by_id(osm, id); if(!member->relation) { member->type = NODE_ID; member->id = id; @@ -755,7 +760,7 @@ /* ----------------------- generic xml handling -------------------------- */ -/* parse loc entry */ +/* parse osm entry */ static void osm_parse_osm(osm_t *osm, xmlDocPtr doc, xmlNode * a_node) { xmlNode *cur_node = NULL; @@ -837,6 +842,8 @@ /* allocate memory to hold osm file description */ osm = g_new0(osm_t, 1); + osm->node_hash = g_new0(hash_table_t, 1); + osm->way_hash = g_new0(hash_table_t, 1); for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { @@ -874,9 +881,34 @@ /* ------------------ osm handling ----------------- */ +/* the two hash tables eat over 512kBytes memory and may thus be */ +/* freed at any time. osm2go can work without them (albeit slower) */ +static void hash_table_free(hash_table_t *table) { + if(!table) return; + + int i; + for(i=0;i<65536;i++) { + hash_item_t *item = table->hash[i]; + while(item) { + hash_item_t *next = item->next; + g_free(item); + item = next; + } + } +} + +void osm_hash_tables_free(osm_t *osm) { + hash_table_free(osm->node_hash); + osm->node_hash = NULL; + hash_table_free(osm->way_hash); + osm->way_hash = NULL; +} + void osm_free(icon_t **icon, osm_t *osm) { if(!osm) return; + osm_hash_tables_free(osm); + if(osm->bounds) osm_bounds_free(osm->bounds); if(osm->user) osm_users_free(osm->user); if(osm->way) osm_ways_free(osm->way); @@ -1057,6 +1089,15 @@ pos2lpos(osm->bounds, &node->pos, &node->lpos); + /* append node to end of hash table if present */ + if(osm->node_hash) { + hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)]; + while(*item) item = &(*item)->next; + + *item = g_new0(hash_item_t, 1); + (*item)->data.node = node; + } + /* just an empty element? then return the node as it is */ if(xmlTextReaderIsEmptyElement(reader)) return node; @@ -1094,14 +1135,9 @@ node_chain_t *node_chain = g_new0(node_chain_t, 1); /* search matching node */ - node_chain->node = osm->node; - while(node_chain->node && node_chain->node->id != id) - node_chain->node = node_chain->node->next; - + node_chain->node = osm_get_node_by_id(osm, id); if(!node_chain->node) printf("Node id %lu not found\n", id); - - if(node_chain->node) - node_chain->node->ways++; + else node_chain->node->ways++; xmlFree(prop); @@ -1138,6 +1174,15 @@ xmlFree(prop); } + /* append way to end of hash table if present */ + if(osm->way_hash) { + hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)]; + while(*item) item = &(*item)->next; + + *item = g_new0(hash_item_t, 1); + (*item)->data.way = way; + } + /* just an empty element? then return the way as it is */ /* (this should in fact never happen as this would be a way without nodes) */ if(xmlTextReaderIsEmptyElement(reader)) @@ -1193,10 +1238,7 @@ case WAY: /* search matching way */ - member->way = osm->way; - while(member->way && member->way->id != id) - member->way = member->way->next; - + member->way = osm_get_way_by_id(osm, id); if(!member->way) { member->type = WAY_ID; member->id = id; @@ -1205,10 +1247,7 @@ case NODE: /* search matching node */ - member->node = osm->node; - while(member->node && member->node->id != id) - member->node = member->node->next; - + member->node = osm_get_node_by_id(osm, id); if(!member->node) { member->type = NODE_ID; member->id = id; @@ -1217,10 +1256,7 @@ case RELATION: /* search matching relation */ - member->relation = osm->relation; - while(member->relation && member->relation->id != id) - member->relation = member->relation->next; - + member->relation = osm_get_relation_by_id(osm, id); if(!member->relation) { member->type = NODE_ID; member->id = id; @@ -1306,6 +1342,8 @@ static osm_t *process_osm(xmlTextReaderPtr reader) { /* alloc osm structure */ osm_t *osm = g_new0(osm_t, 1); + osm->node_hash = g_new0(hash_table_t, 1); + osm->way_hash = g_new0(hash_table_t, 1); node_t **node = &osm->node; way_t **way = &osm->way; @@ -1500,6 +1538,15 @@ /* store current tag pointer in userdata for fast access to current tag */ stack->userdata[1] = &node->tag; + /* append node to end of hash table if present */ + if(osm->node_hash) { + hash_item_t **item = &osm->node_hash->hash[ID2HASH(node->id)]; + while(*item) item = &(*item)->next; + + *item = g_new0(hash_item_t, 1); + (*item)->data.node = node; + } + return TRUE; } @@ -1515,14 +1562,9 @@ g_new0(node_chain_t, 1); /* search matching node */ - node_chain->node = osm->node; - while(node_chain->node && node_chain->node->id != id) - node_chain->node = node_chain->node->next; - + node_chain->node = osm_get_node_by_id(osm, id); if(!node_chain->node) printf("Node id %lu not found\n", id); - - if(node_chain->node) - node_chain->node->ways++; + else node_chain->node->ways++; stack->prev->userdata[2] = &node_chain->next; } @@ -1551,6 +1593,15 @@ stack->userdata[1] = &way->tag; stack->userdata[2] = &way->node_chain; + /* append way to end of hash table if present */ + if(osm->way_hash) { + hash_item_t **item = &osm->way_hash->hash[ID2HASH(way->id)]; + while(*item) item = &(*item)->next; + + *item = g_new0(hash_item_t, 1); + (*item)->data.way = way; + } + return TRUE; } @@ -1580,10 +1631,7 @@ case WAY: /* search matching way */ - member->way = osm->way; - while(member->way && member->way->id != id) - member->way = member->way->next; - + member->way = osm_get_way_by_id(osm, id); if(!member->way) { member->type = WAY_ID; member->id = id; @@ -1592,10 +1640,7 @@ case NODE: /* search matching node */ - member->node = osm->node; - while(member->node && member->node->id != id) - member->node = member->node->next; - + member->node = osm_get_node_by_id(osm, id); if(!member->node) { member->type = NODE_ID; member->id = id; @@ -1604,10 +1649,7 @@ case RELATION: /* search matching relation */ - member->relation = osm->relation; - while(member->relation && member->relation->id != id) - member->relation = member->relation->next; - + member->relation = osm_get_relation_by_id(osm, id); if(!member->relation) { member->type = NODE_ID; member->id = id; @@ -1657,6 +1699,9 @@ osm_t *osm = stack->prev->userdata[0] = stack->userdata[0] = g_new0(osm_t, 1); + osm->node_hash = g_new0(hash_table_t, 1); + osm->way_hash = g_new0(hash_table_t, 1); + /* store direct pointers for faster list access */ /* (otherwise we'd have to search the end of the lists for every item */ /* to be attached) */ @@ -1712,7 +1757,6 @@ struct timeval start; gettimeofday(&start, NULL); - #ifdef OSM_STREAM_PARSER LIBXML_TEST_VERSION; @@ -2002,29 +2046,58 @@ return osm_generate_xml(osm, RELATION, relation); } +/* the following three functions are eating much CPU power */ +/* as they search the objects lists. Hashing is supposed to help */ node_t *osm_get_node_by_id(osm_t *osm, item_id_t id) { + if(id > 0 && osm->node_hash) { + // use hash table if present + hash_item_t *item = osm->node_hash->hash[ID2HASH(id)]; + while(item) { + if(item->data.node->id == id) + return item->data.node; + + item = item->next; + } + } + + /* use linear search if no hash tables are present or search in hash table failed */ node_t *node = osm->node; while(node) { if(node->id == id) return node; - + node = node->next; } + return NULL; } way_t *osm_get_way_by_id(osm_t *osm, item_id_t id) { + if(id > 0 && osm->way_hash) { + // use hash table if present + hash_item_t *item = osm->way_hash->hash[ID2HASH(id)]; + while(item) { + if(item->data.way->id == id) + return item->data.way; + + item = item->next; + } + } + + /* use linear search if no hash tables are present or search on hash table failed */ way_t *way = osm->way; while(way) { if(way->id == id) return way; - + way = way->next; } + return NULL; } relation_t *osm_get_relation_by_id(osm_t *osm, item_id_t id) { + // use linear search relation_t *relation = osm->relation; while(relation) { if(relation->id == id)