metadb.cpp

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 /** @file metadb.cpp Implementation of MetaDb class */
00020 
00021 #include <hn/hnprec.h>
00022 #include <hn/metadb.h>
00023 #include <hn/sharedfile.h>
00024 #include <hn/metadata.h>
00025 
00026 // Default constructor
00027 MetaDb::MetaDb() {
00028 }
00029 
00030 // Destructor - this singleton class's destructor is called during application
00031 // shutdown, after exiting from main. Do not rely on any subsystems being
00032 // operational at this point - just perform cleanup.
00033 // The only list we need to clean up here is primary list - all other lists
00034 // only contain pointers to primary list objects.
00035 MetaDb::~MetaDb() {
00036         for (LIter i = m_list.begin(); i != m_list.end(); ++i) {
00037                 delete *i;
00038         }
00039 }
00040 
00041 MetaDb& MetaDb::instance() {
00042         static MetaDb md;
00043         return md;
00044 }
00045 
00046 enum MetaDbOpCodes {
00047         OP_MDB_VERSION = 0x01    //!< version
00048 };
00049 
00050 // load from stream
00051 void MetaDb::load(std::istream &i) try {
00052         if (!i) {
00053                 return;
00054         }
00055         Utils::StopWatch t;
00056 
00057         logTrace(TRACE_MD, "Loading MetaDb from stream.");
00058         uint8_t ver = Utils::getVal<uint8_t>(i);
00059         if (ver != OP_MDB_VERSION) {
00060                 logError("Wrong version found loading MetaDb from stream.");
00061                 throw std::runtime_error(
00062                         "Parse error reading MetaDb from stream."
00063                 );
00064         }
00065         uint32_t count = Utils::getVal<uint32_t>(i);
00066         logTrace(TRACE_MD, boost::format("%d objects to read.") % count);
00067         uint32_t added = 0;
00068         while (count-- && i) {
00069                 uint8_t oc = Utils::getVal<uint8_t>(i);
00070                 Utils::getVal<uint16_t>(i);
00071                 CHECK_THROW(oc == CGComm::OP_METADATA);
00072                 push(new MetaData(i));
00073                 if (count) {
00074                         logTrace(
00075                                 TRACE_MD, boost::format(
00076                                         "Loading MetaDb: %d entries to go."
00077                                 ) % count
00078                         );
00079                 }
00080                 ++added;
00081         }
00082         if (!i) {
00083                 logError("Unexpected end of stream reading MetaDb.");
00084         } else {
00085                 logMsg(
00086                         boost::format(
00087                                 "MetaDb loaded successfully. %d entries added. "
00088                                 "(%dms)"
00089                         ) % added % t
00090                 );
00091         }
00092 } catch (std::exception &e) {
00093         logError(boost::format("Unable to load MetaDb: %s") % e.what());
00094 }
00095 
00096 void MetaDb::save(std::ostream &os) const {
00097         os << *this;
00098 }
00099 
00100 // Write contents to designated output stream
00101 std::ostream& operator<<(std::ostream &o, const MetaDb &md) {
00102         logTrace(TRACE_MD, "Writing MetaDb to stream.");
00103         Utils::putVal<uint8_t>(o, OP_MDB_VERSION);
00104         Utils::putVal<uint32_t>(o, md.m_list.size());
00105         uint16_t count = 0;
00106         for (MetaDb::CLIter i = md.m_list.begin(); i != md.m_list.end(); ++i) {
00107                 o << *(*i);
00108                 ++count;
00109         }
00110         CHECK_THROW(count == md.m_list.size());
00111         return o;
00112 }
00113 
00114 // MetaDb - Adding entries
00115 // -----------------------
00116 
00117 // Try to add a file name to m_filenames map. If the name (and source) already
00118 // exist, the operation will silently fail.
00119 void MetaDb::tryAddFileName(MetaData *source, const std::string &name) {
00120         CHECK_THROW(source != 0);
00121         logTrace(
00122                 METADB, boost::format("void MetaDb::tryAddFileName(%s)")
00123                 % name
00124         );
00125 
00126         std::pair<FNIter, FNIter> i = m_filenames.equal_range(name);
00127         if (i.first == i.second) {
00128                 logTrace(METADB, "-> Inserting.");
00129                 m_filenames.insert(std::make_pair(name, source));
00130         } else {
00131                 bool found = false;
00132                 for (FNIter j = i.first; j != i.second; ++j) {
00133                         if ((*j).second == source) {
00134                                 found = true;
00135                                 break;
00136                         }
00137                 }
00138                 if (found == false) {
00139                         logTrace(METADB, "-> Inserting.");
00140                         m_filenames.insert(std::make_pair(name, source));
00141                 }
00142         }
00143 }
00144 
00145 // Try to add entry to m_nameToSF map
00146 void MetaDb::tryAddFileName(SharedFile *sf, const std::string &name) {
00147         CHECK_THROW(sf != 0);
00148 
00149         logTrace(
00150                 METADB, boost::format("void MetaDb::tryAddFileName(%s)")
00151                 % name
00152         );
00153         std::pair<NTSFIter, NTSFIter> i = m_nameToSF.equal_range(name);
00154         if (i.first != i.second) {
00155                 for (NTSFIter j = i.first; j != i.second; ++j) {
00156                         if ((*j).second == sf) {
00157                                 return;
00158                         }
00159                 }
00160         }
00161 
00162         logTrace(METADB, "-> Inserting.");
00163         m_nameToSF.insert(std::make_pair(name, sf));
00164 
00165         // We need to know of its destruction events
00166         SharedFile::getEventTable().addHandler(
00167                 sf, this, &MetaDb::onSharedFileEvent
00168         );
00169 }
00170 
00171 // Try to add a hashset to Hash <-> MetaData reference map
00172 void MetaDb::tryAddHashSet(MetaData *source, const HashSetBase *hash) {
00173         CHECK_THROW(hash != 0);
00174         CHECK_THROW(source != 0);
00175         logTrace(
00176                 METADB, boost::format("void MetaDb::tryAddHashSet(%s)")
00177                 % hash->getFileHash().decode()
00178         );
00179 
00180         const HashBase &hashToAdd = hash->getFileHash();
00181 
00182         HTMDIter i = m_hashes.find(hashToAdd.getTypeId());
00183         if (i == m_hashes.end()) {
00184                 // Already outer map key doesn't exist - add the outer map
00185                 // entry, add inner map, and add the hash to inner map.
00186                 std::map<HashWrapper, MetaData*> maptoAdd;
00187                 maptoAdd.insert(
00188                         std::make_pair(HashWrapper(&hashToAdd), source)
00189                 );
00190                 m_hashes.insert(
00191                         std::make_pair(hashToAdd.getTypeId(), maptoAdd)
00192                 );
00193         } else {
00194                 // Outer map key found - search in inner map
00195                 HWTMDIter j = (*i).second.find(HashWrapper(&hashToAdd));
00196                 if (j == (*i).second.end()) {
00197                         // Key not found in inner map - insert it.
00198                         (*i).second.insert(
00199                                 std::make_pair(HashWrapper(&hashToAdd), source)
00200                         );
00201                 }
00202         }
00203 }
00204 
00205 // Try to add hashset to Hash <-> SharedFile reference map
00206 void MetaDb::tryAddHashSet(SharedFile *sf, const HashSetBase *hash) {
00207         CHECK_THROW(hash != 0);
00208         CHECK_THROW(sf != 0);
00209 
00210         const HashBase &hashToAdd = hash->getFileHash();
00211 
00212         // Add to m_hashToSF map
00213         HTSFIter i = m_hashToSF.find(hashToAdd.getTypeId());
00214         if (i == m_hashToSF.end()) {
00215                 // Already outer map key doesn't exist - add the outer map
00216                 // entry, add inner map, and add the hash to inner map.
00217                 std::map<HashWrapper, SharedFile*> toAdd;
00218                 toAdd.insert(std::make_pair(HashWrapper(&hashToAdd), sf));
00219                 m_hashToSF.insert(
00220                         std::make_pair(hashToAdd.getTypeId(), toAdd)
00221                 );
00222         } else {
00223                 // Outer map key found - search in inner map
00224                 HWTSFIter j = (*i).second.find(HashWrapper(&hashToAdd));
00225                 if (j == (*i).second.end()) {
00226                         // Key not found in inner map - insert it.
00227                         (*i).second.insert(
00228                                 std::make_pair(HashWrapper(&hashToAdd), sf)
00229                         );
00230                 }
00231         }
00232 
00233         // We need to know of its destruction events
00234         SharedFile::getEventTable().addHandler(
00235                 sf, this, &MetaDb::onSharedFileEvent
00236         );
00237 }
00238 
00239 // Add new MetaData object
00240 void MetaDb::push(MetaData *md) {
00241         CHECK_THROW(md != 0);
00242 
00243         bool added = m_list.insert(md).second;
00244 
00245         // Add cross-reference file names
00246         for (uint32_t i = 0; i < md->getFileNameCount(); ++i) {
00247                 tryAddFileName(md, md->getFileName(i));
00248         }
00249 
00250         // Add cross-reference hashes
00251         for (uint32_t i = 0; i < md->getHashSetCount(); ++i) {
00252                 tryAddHashSet(md, md->getHashSet(i));
00253         }
00254 
00255         if (added) {
00256                 MetaData::getEventTable().addHandler(
00257                         md, this, &MetaDb::onMetaDataEvent
00258                 );
00259         }
00260 }
00261 
00262 // Add new MetaData object and associate it with SharedFile lookups
00263 void MetaDb::push(MetaData *md, SharedFile *sf) {
00264         CHECK_THROW(md != 0);
00265 
00266         push(md);
00267 
00268         bool added = m_sfToMd.insert(std::make_pair(sf, md)).second;
00269 
00270         for (uint32_t i = 0; i < md->getHashSetCount(); ++i) {
00271                 tryAddHashSet(sf, md->getHashSet(i));
00272         }
00273 
00274         for (uint32_t i = 0; i < md->getFileNameCount(); ++i) {
00275                 tryAddFileName(sf, md->getFileName(i));
00276         }
00277 
00278         if (added) {
00279                 SharedFile::getEventTable().addHandler(
00280                         sf, this, &MetaDb::onSharedFileEvent
00281                 );
00282         }
00283 }
00284 
00285 // MetaDb - Finding entries
00286 // ------------------------
00287 
00288 // Locate MetaData by searching with file hash by looking at m_hashes map
00289 MetaData* MetaDb::find(const HashBase &h) const {
00290         logTrace(
00291                 METADB, boost::format("Searching for %s: %s")
00292                 % h.getType() % h.decode()
00293         );
00294 
00295         CHTMDIter i = m_hashes.find(h.getTypeId());
00296 
00297         if (i == m_hashes.end()) {
00298                 // outer map key not found
00299                 return 0;
00300         }
00301 
00302         CHWTMDIter j = (*i).second.find(HashWrapper(&h));
00303         if (j == (*i).second.end()) {
00304                 // Inner map key not found
00305                 return 0;
00306         }
00307 
00308         logTrace(METADB, "Found.");
00309         return (*j).second;
00310 }
00311 
00312 // Locate MetaData(s) by searching with file name by looking at m_filenames map
00313 std::vector<MetaData*> MetaDb::find(const std::string &filename) const {
00314         logTrace(METADB, boost::format("Searching for filename %s") % filename);
00315 
00316         std::vector<MetaData*> ret;
00317         std::pair<CFNIter, CFNIter> i = m_filenames.equal_range(filename);
00318         if (i.first == i.second) {
00319                 return ret;
00320         }
00321         for (CFNIter j = i.first; j != i.second; ++j) {
00322                 ret.push_back((*j).second);
00323         }
00324         logTrace(METADB, boost::format("%d match(es) found.") % ret.size());
00325         return ret;
00326 }
00327 
00328 // Locate MetaData by searching with SharedFile
00329 MetaData* MetaDb::find(SharedFile *sf) const {
00330         CSFMDIter i = m_sfToMd.find(sf);
00331         if (i == m_sfToMd.end()) {
00332                 return 0;
00333         }
00334         return (*i).second;
00335 }
00336 
00337 // Locate SharedFile by searching with hash
00338 SharedFile* MetaDb::findSharedFile(const HashBase &h) const {
00339         CHTSFIter i = m_hashToSF.find(h.getTypeId());
00340         if (i == m_hashToSF.end()) {
00341                 return 0;
00342         }
00343         HashWrapper hw(&h);
00344         CHWTSFIter j = (*i).second.find(hw);
00345         if (j == (*i).second.end()) {
00346                 return 0;
00347         }
00348         return (*j).second;
00349 }
00350 
00351 // Locate SharedFiless matching given file name
00352 std::vector<SharedFile*> MetaDb::findSharedFile(
00353         const std::string &filename) const
00354 {
00355         std::vector<SharedFile*> ret;
00356         std::pair<CNTSFIter, CNTSFIter> i = m_nameToSF.equal_range(filename);
00357         if (i.first == i.second) {
00358                 return ret;
00359         }
00360         for (CNTSFIter j = i.first; j != i.second; j++) {
00361                 ret.push_back((*j).second);
00362         }
00363         return ret;
00364 }
00365 
00366 // MetaDb - Event Handling
00367 // -----------------------
00368 
00369 void MetaDb::onMetaDataEvent(MetaData *md, int evt) {
00370         CHECK_THROW(md != 0);
00371 
00372         // \todo Uh, yeah, boss, but how do we update SharedFile maps ?
00373         switch (evt) {
00374                 case MD_ADDED_FILENAME:
00375                         for (uint32_t i = 0; i < md->getFileNameCount(); ++i) {
00376                                 tryAddFileName(md, md->getFileName(i));
00377                         }
00378                 case MD_ADDED_HASHSET:
00379                         for (uint32_t i = 0; i < md->getHashSetCount(); ++i) {
00380                                 tryAddHashSet(md, md->getHashSet(i));
00381                         }
00382                 default:
00383                         break;
00384         }
00385 }
00386 
00387 void MetaDb::onSharedFileEvent(SharedFile *sf, int evt) {
00388         if (evt != SF_DESTROY) {
00389                 return;
00390         }
00391         MetaData *md = 0;
00392 
00393         // Remove from m_sfToMD map, remembering MetaData pointer
00394         SFMDIter iter = m_sfToMd.find(sf);
00395         if (iter != m_sfToMd.end()) {
00396                 md = (*iter).second;
00397                 m_sfToMd.erase(iter);
00398         }
00399         if (!md) {
00400                 logWarning(
00401                         "onSharedFileEvent: SharedFile doesn't have MetaData."
00402                 );
00403                 return;
00404         }
00405 
00406         // Look up all file names found in metadata and erase them from
00407         // m_nameToSF map
00408         for (uint32_t i = 0; i < md->getFileNameCount(); ++i) {
00409                 NTSFIter j = m_nameToSF.find(md->getFileName(i));
00410                 if (j == m_nameToSF.end()) {
00411                         continue;
00412                 }
00413                 m_nameToSF.erase(j);
00414         }
00415 
00416         // Look up all hash sets foudn in metadata and erase them from
00417         // m_hashToSF map
00418         for (uint32_t i = 0; i < md->getHashSetCount(); ++i) {
00419                 HTSFIter j = m_hashToSF.find(
00420                         md->getHashSet(i)->getFileHashTypeId()
00421                 );
00422                 if (j == m_hashToSF.end()) {
00423                         continue;
00424                 }
00425                 HWTSFIter k = (*j).second.find(
00426                         HashWrapper(&(md->getHashSet(i)->getFileHash()))
00427                 );
00428                 if (k == (*j).second.end()) {
00429                         continue;
00430                 }
00431                 (*j).second.erase(k);
00432                 if ((*j).second.size() == 0) {
00433                         m_hashToSF.erase(j);
00434                 }
00435         }
00436 }
00437 
00438 // Clears the entire contents of the Db.
00439 void MetaDb::clear() {
00440         for (LIter i = m_list.begin(); i != m_list.end(); ++i) {
00441                 delete *i;
00442         }
00443         m_list.clear();
00444         m_sfToMd.clear();
00445         m_filenames.clear();
00446         m_nameToSF.clear();
00447         m_hashToSF.clear();
00448         m_hashes.clear();
00449 }
00450 
00451 // "From down here we can make the whole wall collapse!"
00452 // "Uh, yeah, boss, but how do we get out?"
00453 //                              -- Goblin Digging Team