diff --git a/ReleaseNotes2_2.md b/ReleaseNotes2_2.md index 618499b..2b335f3 100644 --- a/ReleaseNotes2_2.md +++ b/ReleaseNotes2_2.md @@ -129,8 +129,6 @@ Both network files are available [here](https://doi.org/10.23719/1375314). ## New API functions |Function|Description| |--|--| -|`ENaddlink`| | -|`ENaddnode`| | |`ENgetcurvetype`| | |`ENgetdemandmodel`|| |`ENsetdemandmodel`|| @@ -156,6 +154,8 @@ Both network files are available [here](https://doi.org/10.23719/1375314). |`ENaddlink`|| |`ENdeletelink`|| |`ENdeletenode`|| +| `ENsetnodeid` || +| `ENsetlinkid` || |`ENgetdemandname`|| |`ENsetdemandname`|| diff --git a/src/epanet.c b/src/epanet.c index aa2da3b..2c9017b 100644 --- a/src/epanet.c +++ b/src/epanet.c @@ -89,9 +89,9 @@ This module calls the following functions that reside in other modules: writelogo() writereport() HASH.C - ENHashTablecreate() - ENHashTableFind() - ENHashTableFree() + hashtable_create() + hashtable_find() + hashtable_free() The macro ERRCODE(x) is defined in TYPES.H. It says if the current value of the error code variable (errcode) is not fatal (< 100) then @@ -855,9 +855,8 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) writetime(p, FMT105); } freedata(p); - + out = &p->out_files; - if (out->TmpOutFile != out->OutFile) { if (out->TmpOutFile != NULL) { fclose(out->TmpOutFile); @@ -887,7 +886,7 @@ int DLLEXPORT EN_close(EN_ProjectHandle ph) remove(out->HydFname); if (out->Outflag == SCRATCH) remove(out->OutFname); - + p->Openflag = FALSE; p->hydraulics.OpenHflag = FALSE; p->save_options.SaveHflag = FALSE; @@ -2916,15 +2915,15 @@ int DLLEXPORT EN_setnodeid(EN_ProjectHandle ph, int index, char *newid) if (strcspn(newid, " ;") < n) return set_error(p->error_handle, 209); // Check if another node with same name exists - if (ENHashTableFind(net->NodeHashTable, newid) > 0) + if (hashtable_find(net->NodeHashTable, newid) > 0) { return set_error(p->error_handle, 215); } // Replace the existing node ID with the new value - ENHashTableDelete(net->NodeHashTable, net->Node[index].ID); + hashtable_delete(net->NodeHashTable, net->Node[index].ID); strncpy(net->Node[index].ID, newid, MAXID); - ENHashTableInsert(net->NodeHashTable, net->Node[index].ID, index); + hashtable_insert(net->NodeHashTable, net->Node[index].ID, index); return set_error(p->error_handle, 0); } @@ -3210,15 +3209,15 @@ int DLLEXPORT EN_setlinkid(EN_ProjectHandle ph, int index, char *newid) if (strcspn(newid, " ;") < n) return set_error(p->error_handle, 211); // Check if another link with same name exists - if (ENHashTableFind(net->LinkHashTable, newid) > 0) + if (hashtable_find(net->LinkHashTable, newid) > 0) { return set_error(p->error_handle, 215); } // Replace the existing link ID with the new value - ENHashTableDelete(net->LinkHashTable, net->Link[index].ID); + hashtable_delete(net->LinkHashTable, net->Link[index].ID); strncpy(net->Link[index].ID, newid, MAXID); - ENHashTableInsert(net->LinkHashTable, net->Link[index].ID, index); + hashtable_insert(net->LinkHashTable, net->Link[index].ID, index); return set_error(p->error_handle, 0); } @@ -4325,8 +4324,8 @@ int allocdata(EN_Project *p) parser_data_t *par; /* Allocate node & link ID hash tables */ - p->network.NodeHashTable = ENHashTableCreate(); - p->network.LinkHashTable = ENHashTableCreate(); + p->network.NodeHashTable = hashtable_create(); + p->network.LinkHashTable = hashtable_create(); ERRCODE(MEMCHECK(p->network.NodeHashTable)); ERRCODE(MEMCHECK(p->network.LinkHashTable)); @@ -4498,14 +4497,14 @@ void freedata(EN_Project *p) } free(net->Node); } - + /* Free memory for other network objects */ free(net->Link); free(net->Tank); free(net->Pump); free(net->Valve); free(net->Control); - + /* Free memory for time patterns */ if (net->Pattern != NULL) { for (j = 0; j <= par->MaxPats; j++) @@ -4521,20 +4520,19 @@ void freedata(EN_Project *p) } free(net->Curve); } - + /* Free memory for node coordinates */ if (p->parser.Coordflag == TRUE) { free(net->Coord); } - + /* Free memory for rule base (see RULES.C) */ freerules(p); - + /* Free hash table memory */ - if (net->NodeHashTable != NULL) - ENHashTableFree(net->NodeHashTable); - if (net->LinkHashTable != NULL) - ENHashTableFree(net->LinkHashTable); + if (net->NodeHashTable != NULL) hashtable_free(net->NodeHashTable); + + if (net->LinkHashTable != NULL) hashtable_free(net->LinkHashTable); } /* @@ -4637,7 +4635,7 @@ int findnode(EN_Network *n, char *id) **---------------------------------------------------------------- */ { - return (ENHashTableFind(n->NodeHashTable, id)); + return (hashtable_find(n->NodeHashTable, id)); } int findlink(EN_Network *n, char *id) @@ -4649,7 +4647,7 @@ int findlink(EN_Network *n, char *id) **---------------------------------------------------------------- */ { - return (ENHashTableFind(n->LinkHashTable, id)); + return (hashtable_find(n->LinkHashTable, id)); } int findtank(EN_Network *n, int index) @@ -5055,7 +5053,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { // shift rest of Node array for (index = net->Nnodes; index >= net->Njuncs; index--) { - ENHashTableUpdate(net->NodeHashTable, net->Node[index].ID, index + 1); + hashtable_update(net->NodeHashTable, net->Node[index].ID, index + 1); net->Node[index + 1] = net->Node[index]; net->Coord[index + 1] = net->Coord[index]; } @@ -5171,7 +5169,7 @@ int DLLEXPORT EN_addnode(EN_ProjectHandle ph, char *id, EN_NodeType nodeType) { coord->Y = 0; /* Insert new node into hash table */ - ENHashTableInsert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ + hashtable_insert(net->NodeHashTable, node->ID, nIdx); /* see HASH.C */ return set_error(p->error_handle, 0); } @@ -5194,8 +5192,8 @@ int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, ch return set_error(p->error_handle, 215); /* Lookup the from and to nodes */ - N1 = ENHashTableFind(net->NodeHashTable, fromNode); - N2 = ENHashTableFind(net->NodeHashTable, toNode); + N1 = hashtable_find(net->NodeHashTable, fromNode); + N2 = hashtable_find(net->NodeHashTable, toNode); if (N1 == 0 || N2 == 0) { return set_error(p->error_handle, 203); @@ -5277,7 +5275,7 @@ int DLLEXPORT EN_addlink(EN_ProjectHandle ph, char *id, EN_LinkType linkType, ch link->Rpt = 0; strcpy(link->Comment, ""); - ENHashTableInsert(net->LinkHashTable, link->ID, n); + hashtable_insert(net->LinkHashTable, link->ID, n); return set_error(p->error_handle, 0); } @@ -5300,15 +5298,14 @@ int DLLEXPORT EN_deletelink(EN_ProjectHandle ph, int index) { link = &net->Link[index]; // remove from hash table - ENHashTableDelete(net->LinkHashTable, link->ID); - ENHashTableFind(net->LinkHashTable, link->ID); + hashtable_delete(net->LinkHashTable, link->ID); // shift link and pump arrays to re-sort link indices net->Nlinks--; for (i = index; i <= net->Nlinks - 1; i++) { net->Link[i] = net->Link[i + 1]; // update hashtable - ENHashTableUpdate(net->LinkHashTable, net->Link[i].ID, i); + hashtable_update(net->LinkHashTable, net->Link[i].ID, i); } for (i = 1; i <= net->Npumps; i++) { if (net->Pump[i].Link > index) { @@ -5361,14 +5358,14 @@ int DLLEXPORT EN_deletenode(EN_ProjectHandle ph, int index) { // TODO: check for existing controls/rules that reference this node? // remove from hash table - ENHashTableDelete(net->NodeHashTable, net->Node[index].ID); + hashtable_delete(net->NodeHashTable, net->Node[index].ID); // shift node and coord array to remove node for (i = index; i <= net->Nnodes - 1; i++) { net->Node[i] = net->Node[i + 1]; net->Coord[i] = net->Coord[i + 1]; // update hashtable - ENHashTableUpdate(net->NodeHashTable, net->Node[i].ID, i); + hashtable_update(net->NodeHashTable, net->Node[i].ID, i); } // update tank array diff --git a/src/hash.c b/src/hash.c index ac31246..2d007e2 100755 --- a/src/hash.c +++ b/src/hash.c @@ -1,21 +1,20 @@ /*----------------------------------------------------------------------------- ** hash.c ** - ** Implementation of a simple Hash Table for string storage & retrieval + ** Implementation of a simple Hash Table that uses a string pointer + ** as a key and an associated integer as data. ** ** Written by L. Rossman - ** Last Updated on 6/19/03 + ** Last Updated on 10/17/18 ** - ** The hash table data structure (HTable) is defined in "hash.h". ** Interface Functions: - ** HTcreate() - creates a hash table - ** HTinsert() - inserts a string & its index value into a hash table - ** HTfind() - retrieves the index value of a string from a table - ** HTfree() - frees a hash table + ** hashtable_create - creates a hash table + ** hashtable_insert - inserts a string & its data value into a table + ** hashtable_find - retrieves the data value associated with a string + ** hashtable_findkey - retrieves the key associated with a data value + ** hashtable_delete - deletes an entry from a table + ** hashtable_free - frees a hash table ** - ********************************************************************* - ** NOTE: This is a modified version of the original HASH.C module. - ********************************************************************* */ #ifndef __APPLE__ @@ -26,144 +25,148 @@ #include #include "hash.h" -unsigned int _enHash(char *str); -unsigned int _enHash(char *str) -{ - unsigned int hash = 5381; - unsigned int retHash; - int c; - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - } - retHash = hash % ENHASHTABLEMAXSIZE; - - return retHash; -} +#define HASHTABLEMAXSIZE 128000 -ENHashTable *ENHashTableCreate() +typedef struct DataEntryStruct { - int i; - ENHashTable *ht = (ENHashTable *) calloc(ENHASHTABLEMAXSIZE, sizeof(ENHashTable)); - if (ht != NULL) { - for (i=0; i= ENHASHTABLEMAXSIZE ) { - return(0); - } - entry = (ENHashEntry *) malloc(sizeof(ENHashEntry)); - if (entry == NULL) { - return(0); - } - len = strlen(key) + 1; - entry->key = calloc(len, sizeof(char)); - strncpy(entry->key, key, len); - entry->data = data; - entry->next = ht[i]; - ht[i] = entry; - return(1); -} - -/* Abel Heinsbroek: Added function to update the hash table value for a given key */ -int ENHashTableUpdate(ENHashTable *ht, char *key, int new_data) -{ - unsigned int i = _enHash(key); - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NOTFOUND); - } - entry = ht[i]; - while (entry != NULL) - { - if ( strcmp(entry->key,key) == 0 ) { - entry->data = new_data; - return(1); + int i; + HashTable *ht = (HashTable *) calloc(HASHTABLEMAXSIZE, sizeof(HashTable)); + if (ht != NULL) + { + for (i = 0; i < HASHTABLEMAXSIZE; i++) ht[i] = NULL; } - entry = entry->next; - } - return(NOTFOUND); + return ht; } -int ENHashTableDelete(ENHashTable *ht, char *key) { - unsigned int i = _enHash(key); - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NOTFOUND); - } - entry = ht[i]; - while (entry != NULL) - { - if (strcmp(entry->key, key) == 0) { - entry->key = ""; - return(1); - } - entry = entry->next; - } - - return(NOTFOUND); -} - -int ENHashTableFind(ENHashTable *ht, char *key) +int hashtable_insert(HashTable *ht, char *key, int data) { - unsigned int i = _enHash(key); - - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NOTFOUND); - } - - entry = ht[i]; - while (entry != NULL) - { - if ( strcmp(entry->key,key) == 0 ) { - return(entry->data); - } - entry = entry->next; - } - return(NOTFOUND); + unsigned int i = gethash(key); + DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return 0; + entry = (DataEntry *) malloc(sizeof(DataEntry)); + if (entry == NULL) return(0); + entry->key = key; + entry->data = data; + entry->next = ht[i]; + ht[i] = entry; + return 1; } -char *ENHashTableFindKey(ENHashTable *ht, char *key) +int hashtable_update(HashTable *ht, char *key, int new_data) { - unsigned int i = _enHash(key); - ENHashEntry *entry; - if ( i >= ENHASHTABLEMAXSIZE ) { - return(NULL); - } - entry = ht[i]; - while (entry != NULL) - { - if ( strcmp(entry->key,key) == 0 ) { - return(entry->key); - } - entry = entry->next; - } - return(NULL); -} - -void ENHashTableFree(ENHashTable *ht) -{ - ENHashEntry *entry, *nextentry; - int i; - for (i=0; i= HASHTABLEMAXSIZE ) return NOTFOUND; entry = ht[i]; while (entry != NULL) { - nextentry = entry->next; - free(entry->key); - free(entry); - entry = nextentry; + if ( strcmp(entry->key, key) == 0 ) + { + entry->data = new_data; + return 1; + } + entry = entry->next; } - } - free(ht); + return NOTFOUND; +} + +int hashtable_delete(HashTable *ht, char *key) +{ + unsigned int i = gethash(key); + DataEntry *entry, *preventry; + + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; + + // Check if first entry in bucket i to be deleted + entry = ht[i]; + if (strcmp(entry->key, key) == 0) + { + ht[i] = entry->next; + free(entry); + return 1; + } + + // Check remaining entries in bucket i + preventry = ht[i]; + entry = ht[i]->next; + while (entry != NULL) + { + if (strcmp(entry->key, key) == 0) + { + preventry->next = entry->next; + free(entry); + return 1; + } + preventry = entry; + entry = entry->next; + } + return NOTFOUND; +} + +int hashtable_find(HashTable *ht, char *key) +{ + unsigned int i = gethash(key); + DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return NOTFOUND; + entry = ht[i]; + while (entry != NULL) + { + if ( strcmp(entry->key, key) == 0 ) + { + return entry->data; + } + entry = entry->next; + } + return NOTFOUND; +} + +char *hashtable_findkey(HashTable *ht, char *key) +{ + unsigned int i = gethash(key); + DataEntry *entry; + if ( i >= HASHTABLEMAXSIZE ) return NULL; + entry = ht[i]; + while (entry != NULL) + { + if ( strcmp(entry->key, key) == 0 ) return entry->key; + entry = entry->next; + } + return NULL; +} + +void hashtable_free(HashTable *ht) +{ + DataEntry *entry, *nextentry; + int i; + for (i = 0; i < HASHTABLEMAXSIZE; i++) + { + entry = ht[i]; + while (entry != NULL) + { + nextentry = entry->next; + free(entry); + entry = nextentry; + } + } + free(ht); } diff --git a/src/hash.h b/src/hash.h index 1285c11..612b8d3 100755 --- a/src/hash.h +++ b/src/hash.h @@ -7,24 +7,16 @@ #ifndef HASH_H #define HASH_H -#define ENHASHTABLEMAXSIZE 128000 #define NOTFOUND 0 -typedef struct HTentryStruct -{ - char *key; - int data; - struct HTentryStruct *next; -} ENHashEntry; +typedef struct DataEntryStruct *HashTable; -typedef ENHashEntry *ENHashTable; +HashTable *hashtable_create(void); +int hashtable_insert(HashTable *, char *, int); +int hashtable_find(HashTable *, char *); +char *hashtable_findkey(HashTable *, char *); +void hashtable_free(HashTable *); +int hashtable_update(HashTable *ht, char *key, int new_data); +int hashtable_delete(HashTable *ht, char *key); -ENHashTable *ENHashTableCreate(void); -int ENHashTableInsert(ENHashTable *, char *, int); -int ENHashTableFind(ENHashTable *, char *); -char *ENHashTableFindKey(ENHashTable *, char *); -void ENHashTableFree(ENHashTable *); -int ENHashTableUpdate(ENHashTable *ht, char *key, int new_data); -int ENHashTableDelete(ENHashTable *ht, char *key); - #endif diff --git a/src/input2.c b/src/input2.c index 717ef81..d141d72 100644 --- a/src/input2.c +++ b/src/input2.c @@ -472,7 +472,7 @@ int addnodeID(EN_Network *net, int n, char *id) return (0); /* see EPANET.C */ } strncpy(net->Node[n].ID, id, MAXID); - ENHashTableInsert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */ + hashtable_insert(net->NodeHashTable, net->Node[n].ID, n); /* see HASH.C */ return (1); } @@ -490,7 +490,7 @@ int addlinkID(EN_Network *net, int n, char *id) return (0); /* see EPANET.C */ } strncpy(net->Link[n].ID, id, MAXID); - ENHashTableInsert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */ + hashtable_insert(net->LinkHashTable, net->Link[n].ID, n); /* see HASH.C */ return (1); } diff --git a/src/types.h b/src/types.h index 7b9358d..1984b28 100755 --- a/src/types.h +++ b/src/types.h @@ -874,9 +874,10 @@ typedef struct { Scurve *Curve; /* Curve data */ Scoord *Coord; /* Coordinate data */ Scontrol *Control; /* Control data */ - ENHashTable *NodeHashTable, - *LinkHashTable; /* Hash tables for ID labels */ - Padjlist *Adjlist; /* Node adjacency lists */ + HashTable + *NodeHashTable, + *LinkHashTable; /* Hash tables for ID labels */ + Padjlist *Adjlist; /* Node adjacency lists */ } EN_Network; diff --git a/tests/test_setid.cpp b/tests/test_setid.cpp index bd50a6e..5a03b01 100644 --- a/tests/test_setid.cpp +++ b/tests/test_setid.cpp @@ -1,6 +1,4 @@ -// -// test_setid.cpp -// +// Test of ENsetid EPANET API Function /* This is a test for the API functions that change a node or link ID name. @@ -10,77 +8,76 @@ A node and link name are changed, the network is saved, reopened and the new nam #define BOOST_TEST_MODULE "toolkit" #include -#include +#include #include "epanet2.h" -// NOTE: Project Home needs to be updated to run unit test -#define DATA_PATH_INP "./net1.inp" -#define DATA_PATH_RPT "./test.rpt" -#define DATA_PATH_OUT "./test.out" +#define DATA_PATH_INP "net1.inp" +#define DATA_PATH_RPT "test.rpt" +#define DATA_PATH_OUT "" using namespace std; BOOST_AUTO_TEST_SUITE (test_toolkit) BOOST_AUTO_TEST_CASE(test_setid) +{ + +int main(int argc, char *argv[]) { int error = 0; int index; - char errmsg[256]; - char id[80]; - char newid[80]; + string newid; std::string path_inp = std::string(DATA_PATH_INP); std::string path_rpt = std::string(DATA_PATH_RPT); std::string path_out = std::string(DATA_PATH_OUT); - error = ENopen(path_inp.c_str(), path_rpt.c_str(), path_out.c_str()); + error = ENopen(path_inp.c_str(), path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Test of illegal node name change - error = ENgetnodeid(3, id); - BOOST_REQUIRE(error == 0); - strncpy(newid, "Illegal; node name", 79); - error = ENsetnodeid(3, newid); + newid = "Illegal; node name"; + error = ENsetnodeid(3, (char *)newid.c_str()); BOOST_REQUIRE(error > 0); // Test of legal node name change - strncpy(newid, "Node3", 79); - error = ENsetnodeid(3, newid); + newid = "Node3"; + error = ENsetnodeid(3, (char *)newid.c_str()); BOOST_REQUIRE(error == 0); - + // Test of illegal link name change - error = ENgetlinkid(3, id); - BOOST_REQUIRE(error == 0); - strncpy(newid, "Illegal; link name", 79); - error = ENsetlinkid(3, newid); + newid = "Illegal; link name"; + error = ENsetlinkid(3, (char *)newid.c_str()); BOOST_REQUIRE(error > 0); // Test of legal link name change - strncpy(newid, "Link3", 79); - error = ENsetlinkid(3, newid); + newid = "Link3"; + error = ENsetlinkid(3, (char *)newid.c_str()); BOOST_REQUIRE(error == 0); // Save the project error = ENsaveinpfile("net1_setid.inp"); BOOST_REQUIRE(error == 0); + error = ENclose(); BOOST_REQUIRE(error == 0); - + // Re-open the saved project - error = ENopen("net1_setid.inp", path_rpt.c_str(), path_out.c_str()); + error = ENopen("net1_setid.inp", path_rpt.c_str(), ""); BOOST_REQUIRE(error == 0); // Check that 3rd node has its new name - error = ENgetnodeindex((char *)"Node3", &index); + error = ENgetnodeindex("Node3", &index); BOOST_REQUIRE(error == 0); BOOST_REQUIRE(index == 3); // Check that 3rd link has its new name - error = ENgetlinkindex((char *)"Link3", &index); + error = ENgetlinkindex("Link3", &index); BOOST_REQUIRE(error == 0); BOOST_REQUIRE(index == 3); - ENclose(); + + error = ENclose(); + BOOST_REQUIRE(error == 0); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END()