| 1 | |
|---|
| 2 | #include "ps_database.hxx" |
|---|
| 3 | |
|---|
| 4 | using namespace std; |
|---|
| 5 | |
|---|
| 6 | void PS_Database::reinit(const char *_name, bool _readonly) { |
|---|
| 7 | if (db_file) { |
|---|
| 8 | db_file->reinit(_name, _readonly); |
|---|
| 9 | } |
|---|
| 10 | else { |
|---|
| 11 | db_file = new PS_FileBuffer(_name, _readonly); |
|---|
| 12 | } |
|---|
| 13 | db_name2id_map.clear(); |
|---|
| 14 | db_id2name_map.clear(); |
|---|
| 15 | if (!db_rootnode.isNull()) db_rootnode.setNull(); |
|---|
| 16 | db_rootnode = new PS_Node(-1); |
|---|
| 17 | } |
|---|
| 18 | |
|---|
| 19 | void PS_Database::readMappings(PS_FileBuffer *_file, ID2NameMap &_id2name_map, Name2IDMap &_name2id_map) { |
|---|
| 20 | char *buffer = (char *)malloc(_file->BUFFER_SIZE); |
|---|
| 21 | // read number of species |
|---|
| 22 | unsigned long int number_of_species = 0; |
|---|
| 23 | _file->get_ulong(number_of_species); |
|---|
| 24 | // read mappings |
|---|
| 25 | for (unsigned long int i = 0; i < number_of_species; ++i) { |
|---|
| 26 | // read id |
|---|
| 27 | SpeciesID id; |
|---|
| 28 | _file->get(&id, sizeof(SpeciesID)); |
|---|
| 29 | // read name |
|---|
| 30 | unsigned int length_of_name; |
|---|
| 31 | _file->get_uint(length_of_name); |
|---|
| 32 | _file->get(buffer, length_of_name); |
|---|
| 33 | // store in mappings |
|---|
| 34 | string name(buffer, length_of_name); |
|---|
| 35 | _id2name_map[id] = name; |
|---|
| 36 | _name2id_map[name] = id; |
|---|
| 37 | } |
|---|
| 38 | free(buffer); |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | void PS_Database::writeMappings(PS_FileBuffer *_file, ID2NameMap &_id2name_map) { |
|---|
| 42 | // write number of species |
|---|
| 43 | _file->put_ulong(_id2name_map.size()); |
|---|
| 44 | // write mappings |
|---|
| 45 | for (ID2NameMapCIter i = _id2name_map.begin(); i != _id2name_map.end(); ++i) { |
|---|
| 46 | // write id |
|---|
| 47 | _file->put(&(i->first), sizeof(SpeciesID)); |
|---|
| 48 | // write name |
|---|
| 49 | unsigned int length_of_name = i->second.size(); |
|---|
| 50 | _file->put_uint(length_of_name); |
|---|
| 51 | _file->put(i->second.c_str(), length_of_name); |
|---|
| 52 | } |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | void PS_Database::readTree(PS_FileBuffer *_file) { |
|---|
| 56 | if (!db_rootnode.isNull()) db_rootnode.setNull(); // discard old tree |
|---|
| 57 | db_rootnode = new PS_Node(-1); |
|---|
| 58 | db_rootnode->load(_file); |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | void PS_Database::writeTree(PS_FileBuffer *_file) { |
|---|
| 62 | if (db_rootnode.isNull()) return; // no tree, no write |
|---|
| 63 | db_rootnode->save(_file); |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | bool PS_Database::readHeader(PS_FileBuffer *_file) { |
|---|
| 67 | char *buffer = (char *) malloc(FILE_ID.size()); |
|---|
| 68 | _file->get(buffer, FILE_ID.size()); |
|---|
| 69 | bool file_ok = (FILE_ID.compare(buffer) == 0); |
|---|
| 70 | if (buffer) free(buffer); |
|---|
| 71 | return file_ok; |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | void PS_Database::writeHeader(PS_FileBuffer *_file) { |
|---|
| 75 | _file->put(FILE_ID.c_str(), FILE_ID.size()); |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | void PS_Database::callback(void *_caller) { |
|---|
| 79 | // |
|---|
| 80 | // return if node has no probes |
|---|
| 81 | // |
|---|
| 82 | if (!((PS_Node *)_caller)->hasProbes()) return; |
|---|
| 83 | |
|---|
| 84 | // |
|---|
| 85 | // convert IDs from file to DB-IDs |
|---|
| 86 | // |
|---|
| 87 | IDSet path; |
|---|
| 88 | PS_NodePtr current_node = db_path; |
|---|
| 89 | while (current_node->hasChildren()) { |
|---|
| 90 | // get next node in path |
|---|
| 91 | pair<bool, PS_NodePtr> child = current_node->getChild(0); |
|---|
| 92 | ps_assert(child.first); |
|---|
| 93 | current_node = child.second; |
|---|
| 94 | // get ID from node |
|---|
| 95 | SpeciesID id = current_node->getNum(); |
|---|
| 96 | // store ID in ID-Set |
|---|
| 97 | ID2IDMapCIter db_id = db_file2db_id_map.find(id); |
|---|
| 98 | path.insert((db_id == db_file2db_id_map.end()) ? id : db_id->second); |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | // |
|---|
| 102 | // assert path |
|---|
| 103 | // |
|---|
| 104 | current_node = db_rootnode; |
|---|
| 105 | for (IDSetCIter id=path.begin(); id != path.end(); ++id) { |
|---|
| 106 | current_node = current_node->assertChild(*id); |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | // |
|---|
| 110 | // append probes |
|---|
| 111 | // |
|---|
| 112 | current_node->addProbes(((PS_Node *)_caller)->getProbesBegin(), ((PS_Node *)_caller)->getProbesEnd()); |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | bool PS_Database::merge(const char *_other_db_name) { |
|---|
| 116 | // |
|---|
| 117 | // read other DB's mappings |
|---|
| 118 | // |
|---|
| 119 | PS_FileBuffer *other_db_file = new PS_FileBuffer(_other_db_name, PS_FileBuffer::READONLY); |
|---|
| 120 | Name2IDMap other_name2id_map; |
|---|
| 121 | ID2NameMap other_id2name_map; |
|---|
| 122 | |
|---|
| 123 | if (!readHeader(other_db_file)) return false; // not a file i wrote |
|---|
| 124 | readMappings(other_db_file, other_id2name_map, other_name2id_map); |
|---|
| 125 | |
|---|
| 126 | // |
|---|
| 127 | // get next assignable ID from highest used ID in mappings |
|---|
| 128 | // |
|---|
| 129 | SpeciesID next_usable_ID = (db_id2name_map.rbegin()->first > other_id2name_map.rbegin()->first) ? db_id2name_map.rbegin()->first + 1 : other_id2name_map.rbegin()->first + 1; |
|---|
| 130 | |
|---|
| 131 | // |
|---|
| 132 | // iterate over DB names |
|---|
| 133 | // |
|---|
| 134 | db_file2db_id_map.clear(); |
|---|
| 135 | for (Name2IDMapCIter i=db_name2id_map.begin(); i != db_name2id_map.end(); ++i) { |
|---|
| 136 | // lookup name in other mapping |
|---|
| 137 | Name2IDMapIter other_i = other_name2id_map.find(i->first); |
|---|
| 138 | |
|---|
| 139 | // if name not in other mapping |
|---|
| 140 | if (other_i == other_name2id_map.end()) { |
|---|
| 141 | // lookup ID in other mapping |
|---|
| 142 | ID2NameMapIter other_i2 = other_id2name_map.find(i->second); |
|---|
| 143 | |
|---|
| 144 | // if ID is used for other name in other_mappings |
|---|
| 145 | if (other_i2 != other_id2name_map.end()) { |
|---|
| 146 | // lookup other name in DB mapping |
|---|
| 147 | Name2IDMapIter i2 = db_name2id_map.find(other_i2->second); |
|---|
| 148 | |
|---|
| 149 | // if other name is not in DB mapping |
|---|
| 150 | if (i2 == db_name2id_map.end()) { |
|---|
| 151 | // store file->DB ID mapping |
|---|
| 152 | db_file2db_id_map[other_i2->first] = next_usable_ID; |
|---|
| 153 | ++next_usable_ID; |
|---|
| 154 | // erase handled name |
|---|
| 155 | other_name2id_map.erase(other_i2->second); |
|---|
| 156 | } |
|---|
| 157 | } |
|---|
| 158 | } |
|---|
| 159 | // if name in other mapping with different ID |
|---|
| 160 | else if (other_i->second != i->second) { |
|---|
| 161 | // store file->DB ID mapping |
|---|
| 162 | db_file2db_id_map[other_i->second] = i->second; |
|---|
| 163 | // erase handled name |
|---|
| 164 | other_name2id_map.erase(other_i); |
|---|
| 165 | } |
|---|
| 166 | // if name in other mapping with same ID |
|---|
| 167 | else { |
|---|
| 168 | // erase handled name |
|---|
| 169 | other_name2id_map.erase(other_i); |
|---|
| 170 | } |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | // |
|---|
| 174 | // iterate over remaining file names |
|---|
| 175 | // |
|---|
| 176 | for (Name2IDMapCIter other_i=other_name2id_map.begin(); other_i != other_name2id_map.end(); ++other_i) { |
|---|
| 177 | db_file2db_id_map[other_i->second] = next_usable_ID; |
|---|
| 178 | ++next_usable_ID; |
|---|
| 179 | } |
|---|
| 180 | db_MAX_ID = next_usable_ID-1; |
|---|
| 181 | |
|---|
| 182 | // append tree if mappings are equal |
|---|
| 183 | if (db_file2db_id_map.empty()) { |
|---|
| 184 | return db_rootnode->append(other_db_file); |
|---|
| 185 | } |
|---|
| 186 | // merge in tree if mappings differ |
|---|
| 187 | else { |
|---|
| 188 | if (db_path.isNull()) db_path = new PS_Node(-1); |
|---|
| 189 | return db_path->read(other_db_file, this); |
|---|
| 190 | } |
|---|
| 191 | } |
|---|