← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3152: fix dupe hashing

 

------------------------------------------------------------
revno: 3152
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Thu 2012-12-13 18:04:31 +0100
message:
  fix dupe hashing
modified:
  dcpp/HashManager.cpp
  dcpp/HashManager.h
  dcpp/HashValue.h
  dcpp/ShareManager.cpp
  dcpp/ShareManager.h


--
lp:dcplusplus
https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk

Your team Dcplusplus-team is subscribed to branch lp:dcplusplus.
To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'dcpp/HashManager.cpp'
--- dcpp/HashManager.cpp	2012-06-08 15:27:48 +0000
+++ dcpp/HashManager.cpp	2012-12-13 17:04:31 +0000
@@ -36,23 +36,13 @@
 static const uint32_t HASH_FILE_VERSION = 2;
 const int64_t HashManager::MIN_BLOCK_SIZE = 64 * 1024;
 
-bool HashManager::checkTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) {
-	Lock l(cs);
-	if (!store.checkTTH(aFileName, aSize, aTimeStamp)) {
-		hasher.hashFile(aFileName, aSize);
-		return false;
-	}
-	return true;
-}
-
-TTHValue HashManager::getTTH(const string& aFileName, int64_t aSize) {
-	Lock l(cs);
-	const TTHValue* tth = store.getTTH(aFileName);
-	if (tth == NULL) {
-		hasher.hashFile(aFileName, aSize);
-		throw HashException();
-	}
-	return *tth;
+optional<TTHValue> HashManager::getTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) noexcept {
+	Lock l(cs);
+	auto tth = store.getTTH(aFileName, aSize, aTimeStamp);
+	if(!tth) {
+		hasher.hashFile(aFileName, aSize);
+	}
+	return tth;
 }
 
 bool HashManager::getTree(const TTHValue& root, TigerTree& tt) {
@@ -178,39 +168,28 @@
 	return i == treeIndex.end() ? 0 : i->second.getBlockSize();
 }
 
-bool HashManager::HashStore::checkTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) {
+optional<TTHValue> HashManager::HashStore::getTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) noexcept {
 	auto fname = Text::toLower(Util::getFileName(aFileName));
 	auto fpath = Text::toLower(Util::getFilePath(aFileName));
+
 	auto i = fileIndex.find(fpath);
 	if (i != fileIndex.end()) {
 		auto j = find(i->second.begin(), i->second.end(), fname);
 		if (j != i->second.end()) {
 			FileInfo& fi = *j;
-			auto ti = treeIndex.find(fi.getRoot());
-			if (ti == treeIndex.end() || ti->second.getSize() != aSize || fi.getTimeStamp() != aTimeStamp) {
-				i->second.erase(j);
-				dirty = true;
-				return false;
+			const auto& root = fi.getRoot();
+			auto ti = treeIndex.find(root);
+			if(ti != treeIndex.end() && ti->second.getSize() == aSize && fi.getTimeStamp() == aTimeStamp) {
+				fi.setUsed(true);
+				return root;
 			}
-			return true;
-		}
-	}
-	return false;
-}
-
-const TTHValue* HashManager::HashStore::getTTH(const string& aFileName) {
-	auto fname = Text::toLower(Util::getFileName(aFileName));
-	auto fpath = Text::toLower(Util::getFilePath(aFileName));
-
-	auto i = fileIndex.find(fpath);
-	if (i != fileIndex.end()) {
-		auto j = find(i->second.begin(), i->second.end(), fname);
-		if (j != i->second.end()) {
-			j->setUsed(true);
-			return &(j->getRoot());
-		}
-	}
-	return NULL;
+
+			// the file size or the timestamp has changed
+			i->second.erase(j);
+			dirty = true;
+		}
+	}
+	return nullptr;
 }
 
 void HashManager::HashStore::rebuild() {
@@ -462,7 +441,7 @@
 	}
 }
 
-void HashManager::Hasher::hashFile(const string& fileName, int64_t size) {
+void HashManager::Hasher::hashFile(const string& fileName, int64_t size) noexcept {
 	Lock l(cs);
 	if(w.insert(make_pair(fileName, size)).second) {
 		if(paused > 0)
@@ -472,18 +451,18 @@
 	}
 }
 
-bool HashManager::Hasher::pause() {
+bool HashManager::Hasher::pause() noexcept {
 	Lock l(cs);
 	return paused++;
 }
 
-void HashManager::Hasher::resume() {
+void HashManager::Hasher::resume() noexcept {
 	Lock l(cs);
 	while(--paused > 0)
 		s.signal();
 }
 
-bool HashManager::Hasher::isPaused() const {
+bool HashManager::Hasher::isPaused() const noexcept {
 	Lock l(cs);
 	return paused > 0;
 }
@@ -637,17 +616,17 @@
 		HashManager::getInstance()->resumeHashing();
 }
 
-bool HashManager::pauseHashing() {
+bool HashManager::pauseHashing() noexcept {
 	Lock l(cs);
 	return hasher.pause();
 }
 
-void HashManager::resumeHashing() {
+void HashManager::resumeHashing() noexcept {
 	Lock l(cs);
 	hasher.resume();
 }
 
-bool HashManager::isHashingPaused() const {
+bool HashManager::isHashingPaused() const noexcept {
 	Lock l(cs);
 	return hasher.isPaused();
 }

=== modified file 'dcpp/HashManager.h'
--- dcpp/HashManager.h	2012-01-13 20:55:20 +0000
+++ dcpp/HashManager.h	2012-12-13 17:04:31 +0000
@@ -21,6 +21,8 @@
 
 #include <map>
 
+#include <boost/optional.hpp>
+
 #include "Singleton.h"
 #include "MerkleTree.h"
 #include "Thread.h"
@@ -34,6 +36,8 @@
 
 using std::map;
 
+using boost::optional;
+
 STANDARD_EXCEPTION(HashException);
 
 class HashLoader;
@@ -54,17 +58,12 @@
 		hasher.join();
 	}
 
-	/**
-	 * Check if the TTH tree associated with the filename is current.
-	 */
-	bool checkTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp);
+	/** Get the TTH root associated with the filename if its tree is current. */
+	optional<TTHValue> getTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) noexcept;
 
 	void stopHashing(const string& baseDir) { hasher.stopHashing(baseDir); }
 	void setPriority(Thread::Priority p) { hasher.setThreadPriority(p); }
 
-	/** @return TTH root */
-	TTHValue getTTH(const string& aFileName, int64_t aSize);
-
 	bool getTree(const TTHValue& root, TigerTree& tt);
 
 	/** Return block size of the tree associated with root, or 0 if no such tree is in the store */
@@ -102,21 +101,21 @@
 	};
 
 	/// @return whether hashing was already paused
-	bool pauseHashing();
-	void resumeHashing();
-	bool isHashingPaused() const;
+	bool pauseHashing() noexcept;
+	void resumeHashing() noexcept;
+	bool isHashingPaused() const noexcept;
 
 private:
 	class Hasher : public Thread {
 	public:
 		Hasher() : stop(false), running(false), paused(0), rebuild(false), currentSize(0) { }
 
-		void hashFile(const string& fileName, int64_t size);
+		void hashFile(const string& fileName, int64_t size) noexcept;
 
 		/// @return whether hashing was already paused
-		bool pause();
-		void resume();
-		bool isPaused() const;
+		bool pause() noexcept;
+		void resume() noexcept;
+		bool isPaused() const noexcept;
 
 		void stopHashing(const string& baseDir);
 		virtual int run();
@@ -157,10 +156,9 @@
 
 		void rebuild();
 
-		bool checkTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp);
+		optional<TTHValue> getTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) noexcept;
 
 		void addTree(const TigerTree& tt) noexcept;
-		const TTHValue* getTTH(const string& aFileName);
 		bool getTree(const TTHValue& root, TigerTree& tth);
 		size_t getBlockSize(const TTHValue& root) const;
 		bool isDirty() { return dirty; }

=== modified file 'dcpp/HashValue.h'
--- dcpp/HashValue.h	2012-01-13 20:55:20 +0000
+++ dcpp/HashValue.h	2012-12-13 17:04:31 +0000
@@ -32,8 +32,7 @@
 	HashValue() { }
 	explicit HashValue(const uint8_t* aData) { memcpy(data, aData, BYTES); }
 	explicit HashValue(const std::string& base32) { Encoder::fromBase32(base32.c_str(), data, BYTES); }
-	HashValue(const HashValue& rhs) { memcpy(data, rhs.data, BYTES); }
-	HashValue& operator=(const HashValue& rhs) { memcpy(data, rhs.data, BYTES); return *this; }
+
 	bool operator!=(const HashValue& rhs) const { return !(*this == rhs); }
 	bool operator==(const HashValue& rhs) const { return memcmp(data, rhs.data, BYTES) == 0; }
 	bool operator<(const HashValue& rhs) const { return memcmp(data, rhs.data, BYTES) < 0; }

=== modified file 'dcpp/ShareManager.cpp'
--- dcpp/ShareManager.cpp	2012-12-12 22:44:47 +0000
+++ dcpp/ShareManager.cpp	2012-12-13 17:04:31 +0000
@@ -135,9 +135,9 @@
 }
 
 string ShareManager::toVirtual(const TTHValue& tth) const {
-	if(tth == bzXmlRoot) {
+	if(bzXmlRoot && tth == bzXmlRoot) {
 		return Transfer::USER_LIST_NAME_BZ;
-	} else if(tth == xmlRoot) {
+	} else if(xmlRoot && tth == xmlRoot) {
 		return Transfer::USER_LIST_NAME;
 	}
 
@@ -165,8 +165,8 @@
 		return make_pair(getBZXmlFile(), 0);
 	}
 
-	auto i = findFile(virtualFile);
-	return make_pair(i->getRealPath(), i->getSize());
+	auto f = findFile(virtualFile);
+	return make_pair(f.getRealPath(), f.getSize());
 }
 
 StringList ShareManager::getRealPaths(const string& virtualPath) {
@@ -203,7 +203,7 @@
 	return ret;
 }
 
-TTHValue ShareManager::getTTH(const string& virtualFile) const {
+optional<TTHValue> ShareManager::getTTH(const string& virtualFile) const {
 	Lock l(cs);
 	if(virtualFile == Transfer::USER_LIST_NAME_BZ) {
 		return bzXmlRoot;
@@ -211,20 +211,21 @@
 		return xmlRoot;
 	}
 
-	return findFile(virtualFile)->getTTH();
+	return findFile(virtualFile).tth;
 }
 
 MemoryInputStream* ShareManager::getTree(const string& virtualFile) const {
 	TigerTree tree;
 	if(virtualFile.compare(0, 4, "TTH/") == 0) {
 		if(!HashManager::getInstance()->getTree(TTHValue(virtualFile.substr(4)), tree))
-			return 0;
+			return nullptr;
 	} else {
 		try {
-			TTHValue tth = getTTH(virtualFile);
-			HashManager::getInstance()->getTree(tth, tree);
+			auto tth = getTTH(virtualFile);
+			if(!tth) { return nullptr; }
+			HashManager::getInstance()->getTree(*tth, tree);
 		} catch(const Exception&) {
-			return 0;
+			return nullptr;
 		}
 	}
 
@@ -235,18 +236,27 @@
 AdcCommand ShareManager::getFileInfo(const string& aFile) {
 	if(aFile == Transfer::USER_LIST_NAME) {
 		generateXmlList();
+		if(!xmlRoot) {
+			throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
+		}
+
 		AdcCommand cmd(AdcCommand::CMD_RES);
 		cmd.addParam("FN", aFile);
 		cmd.addParam("SI", Util::toString(xmlListLen));
-		cmd.addParam("TR", xmlRoot.toBase32());
+		cmd.addParam("TR", xmlRoot->toBase32());
 		return cmd;
-	} else if(aFile == Transfer::USER_LIST_NAME_BZ) {
+	}
+
+	if(aFile == Transfer::USER_LIST_NAME_BZ) {
 		generateXmlList();
+		if(!bzXmlRoot) {
+			throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
+		}
 
 		AdcCommand cmd(AdcCommand::CMD_RES);
 		cmd.addParam("FN", aFile);
 		cmd.addParam("SI", Util::toString(bzXmlListLen));
-		cmd.addParam("TR", bzXmlRoot.toBase32());
+		cmd.addParam("TR", bzXmlRoot->toBase32());
 		return cmd;
 	}
 
@@ -264,7 +274,7 @@
 	AdcCommand cmd(AdcCommand::CMD_RES);
 	cmd.addParam("FN", f.getADCPath());
 	cmd.addParam("SI", Util::toString(f.getSize()));
-	cmd.addParam("TR", f.getTTH().toBase32());
+	cmd.addParam("TR", f.tth->toBase32());
 	return cmd;
 }
 
@@ -297,13 +307,13 @@
 	return make_pair(d, virtualPath.substr(j));
 }
 
-ShareManager::Directory::File::Set::const_iterator ShareManager::findFile(const string& virtualFile) const {
+const ShareManager::Directory::File& ShareManager::findFile(const string& virtualFile) const {
 	if(virtualFile.compare(0, 4, "TTH/") == 0) {
 		auto i = tthIndex.find(TTHValue(virtualFile.substr(4)));
 		if(i == tthIndex.end()) {
 			throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
 		}
-		return i->second;
+		return *i->second;
 	}
 
 	auto v = splitVirtual(virtualFile);
@@ -311,7 +321,7 @@
 		Directory::File::StringComp(v.second));
 	if(it == v.first->files.end())
 		throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
-	return it;
+	return *it;
 }
 
 string ShareManager::validateVirtual(const string& aVirt) const noexcept {
@@ -683,20 +693,17 @@
 			}
 		} else {
 			// Not a directory, assume it's a file...make sure we're not sharing the settings file...
-			if( (Util::stricmp(name.c_str(), "DCPlusPlus.xml") != 0) &&
-				(Util::stricmp(name.c_str(), "Favorites.xml") != 0)) {
-
-				int64_t size = i->getSize();
-				string fileName = aName + name;
-				if(Util::stricmp(fileName, SETTING(TLS_PRIVATE_KEY_FILE)) == 0) {
-					continue;
-				}
-				try {
-					if(HashManager::getInstance()->checkTTH(fileName, size, i->getLastWriteTime()))
-						lastFileIter = dir->files.insert(lastFileIter, Directory::File(name, size, dir, HashManager::getInstance()->getTTH(fileName, size)));
-				} catch(const HashException&) {
-				}
-			}
+			if(Util::stricmp(name.c_str(), "DCPlusPlus.xml") == 0 ||
+				Util::stricmp(name.c_str(), "Favorites.xml") == 0) { continue; }
+
+			int64_t size = i->getSize();
+			string fileName = aName + name;
+
+			// don't share the private key file
+			if(Util::stricmp(fileName, SETTING(TLS_PRIVATE_KEY_FILE)) == 0) { continue; }
+
+			lastFileIter = dir->files.insert(lastFileIter, Directory::File(name, size, dir,
+				HashManager::getInstance()->getTTH(fileName, size, i->getLastWriteTime())));
 		}
 	}
 
@@ -739,9 +746,14 @@
 void ShareManager::updateIndices(Directory& dir, const Directory::File::Set::iterator& i) {
 	const Directory::File& f = *i;
 
-	auto j = tthIndex.find(f.getTTH());
+	if(!f.tth) {
+		return;
+	}
+
+	auto j = tthIndex.find(*f.tth);
 	if(j == tthIndex.end()) {
-		dir.size+=f.getSize();
+		dir.size += f.getSize();
+
 	} else {
 		if(!SETTING(LIST_DUPES)) {
 			try {
@@ -756,7 +768,7 @@
 
 	dir.addType(getType(f.getName()));
 
-	tthIndex[f.getTTH()] = i;
+	tthIndex[*f.tth] = &f;
 	bloom.add(Text::toLower(f.getName()));
 }
 
@@ -1013,6 +1025,7 @@
 
 void ShareManager::Directory::filesToXml(OutputStream& xmlFile, string& indent, string& tmp2) const {
 	for(auto& f: files) {
+		if(!f.tth) { continue; }
 		xmlFile.write(indent);
 		xmlFile.write(LITERAL("<File Name=\""));
 		xmlFile.write(SimpleXML::escape(f.getName(), tmp2, true));
@@ -1020,7 +1033,7 @@
 		xmlFile.write(Util::toString(f.getSize()));
 		xmlFile.write(LITERAL("\" TTH=\""));
 		tmp2.clear();
-		xmlFile.write(f.getTTH().toBase32(tmp2));
+		xmlFile.write(f.tth->toBase32(tmp2));
 		xmlFile.write(LITERAL("\"/>\r\n"));
 	}
 }
@@ -1166,15 +1179,18 @@
 
 	bool sizeOk = (aSearchType != SearchManager::SIZE_ATLEAST) || (aSize == 0);
 	if( (cur->empty()) &&
-		(((aFileType == SearchManager::TYPE_ANY) && sizeOk) || (aFileType == SearchManager::TYPE_DIRECTORY)) ) {
+		(((aFileType == SearchManager::TYPE_ANY) && sizeOk) || (aFileType == SearchManager::TYPE_DIRECTORY)) )
+	{
 		// We satisfied all the search words! Add the directory...(NMDC searches don't support directory size)
-		SearchResultPtr sr(new SearchResult(SearchResult::TYPE_DIRECTORY, 0, getFullName(), TTHValue()));
+		/// @todo send the directory hash when we have one
+		SearchResultPtr sr(new SearchResult(SearchResult::TYPE_DIRECTORY, 0, getFullName(), TTHValue(string(39, 'A'))));
 		aResults.push_back(sr);
 		ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
 	}
 
 	if(aFileType != SearchManager::TYPE_DIRECTORY) {
 		for(auto& i: files) {
+			if(!i.tth) { continue; }
 
 			if(aSearchType == SearchManager::SIZE_ATLEAST && aSize > i.getSize()) {
 				continue;
@@ -1190,7 +1206,7 @@
 
 			// Check file type...
 			if(checkType(i.getName(), aFileType)) {
-				SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE, i.getSize(), getFullName() + i.getName(), i.getTTH()));
+				SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE, i.getSize(), getFullName() + i.getName(), *i.tth));
 				aResults.push_back(sr);
 				ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
 				if(aResults.size() >= maxResults) {
@@ -1213,7 +1229,7 @@
 			auto i = tthIndex.find(tth);
 			if(i != tthIndex.end()) {
 				SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE, i->second->getSize(),
-					i->second->getParent()->getFullName() + i->second->getName(), i->second->getTTH()));
+					i->second->getParent()->getFullName() + i->second->getName(), *i->second->tth));
 
 				results.push_back(sr);
 				ShareManager::getInstance()->addHits(1);
@@ -1245,7 +1261,7 @@
 }
 
 ShareManager::AdcSearch::AdcSearch(const StringList& params) : include(&includeX), gt(0),
-	lt(numeric_limits<int64_t>::max()), hasRoot(false), isDirectory(false)
+	lt(numeric_limits<int64_t>::max()), isDirectory(false)
 {
 	for(const auto& p: params) {
 		if(p.length() <= 2)
@@ -1253,7 +1269,6 @@
 
 		uint16_t cmd = toCode(p[0], p[1]);
 		if(toCode('T', 'R') == cmd) {
-			hasRoot = true;
 			root = TTHValue(p.substr(2));
 			return;
 		} else if(toCode('A', 'N') == cmd) {
@@ -1324,13 +1339,15 @@
 	bool sizeOk = (aStrings.gt == 0);
 	if( cur->empty() && aStrings.ext.empty() && sizeOk ) {
 		// We satisfied all the search words! Add the directory...
-		SearchResultPtr sr(new SearchResult(SearchResult::TYPE_DIRECTORY, getSize(), getFullName(), TTHValue()));
+		/// @todo send the directory hash when we have one
+		SearchResultPtr sr(new SearchResult(SearchResult::TYPE_DIRECTORY, getSize(), getFullName(), TTHValue(string(39, 'A'))));
 		aResults.push_back(sr);
 		ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
 	}
 
 	if(!aStrings.isDirectory) {
 		for(auto& i: files) {
+			if(!i.tth) { continue; }
 
 			if(!(i.getSize() >= aStrings.gt)) {
 				continue;
@@ -1352,7 +1369,7 @@
 			if(aStrings.hasExt(i.getName())) {
 
 				SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE,
-					i.getSize(), getFullName() + i.getName(), i.getTTH()));
+					i.getSize(), getFullName() + i.getName(), *i.tth));
 				aResults.push_back(sr);
 				ShareManager::getInstance()->addHits(1);
 				if(aResults.size() >= maxResults) {
@@ -1373,12 +1390,12 @@
 
 	Lock l(cs);
 
-	if(srch.hasRoot) {
-		auto i = tthIndex.find(srch.root);
+	if(srch.root) {
+		auto i = tthIndex.find(*srch.root);
 		if(i != tthIndex.end()) {
 			SearchResultPtr sr(new SearchResult(SearchResult::TYPE_FILE,
 				i->second->getSize(), i->second->getParent()->getFullName() + i->second->getName(),
-				i->second->getTTH()));
+				*i->second->tth));
 			results.push_back(sr);
 			addHits(1);
 		}
@@ -1395,9 +1412,9 @@
 	}
 }
 
-ShareManager::Directory::Ptr ShareManager::getDirectory(const string& fname) {
+ShareManager::Directory::Ptr ShareManager::getDirectory(const string& realPath) noexcept {
 	for(auto& mi: shares) {
-		if(Util::strnicmp(fname, mi.first, mi.first.length()) == 0) {
+		if(Util::strnicmp(realPath, mi.first, mi.first.length()) == 0) {
 			Directory::Ptr d;
 			for(auto& i: directories) {
 				if(Util::stricmp(i->getName(), mi.second) == 0) {
@@ -1411,8 +1428,8 @@
 
 			string::size_type i;
 			string::size_type j = mi.first.length();
-			while( (i = fname.find(PATH_SEPARATOR, j)) != string::npos) {
-				auto dmi = d->directories.find(fname.substr(j, i-j));
+			while( (i = realPath.find(PATH_SEPARATOR, j)) != string::npos) {
+				auto dmi = d->directories.find(realPath.substr(j, i-j));
 				j = i + 1;
 				if(dmi == d->directories.end())
 					return Directory::Ptr();
@@ -1424,42 +1441,82 @@
 	return Directory::Ptr();
 }
 
-void ShareManager::on(QueueManagerListener::FileMoved, const string& n) noexcept {
+optional<const ShareManager::Directory::File&> ShareManager::getFile(const string& realPath, Directory::Ptr d) noexcept {
+	if(!d) {
+		d = getDirectory(realPath);
+		if(!d) {
+			return nullptr;
+		}
+	}
+
+	auto i = d->findFile(Util::getFileName(realPath));
+	if(i == d->files.end()) {
+		/* should never happen, but let's fail gracefully (maybe a synchro issue with a dir being
+		removed during hashing...)... */
+		dcdebug("ShareManager::getFile: the file <%s> could not be found, strange!\n", realPath.c_str());
+		return nullptr;
+	}
+
+	if(i->realPath && i->realPath == realPath) {
+		/* lucky! the base file already had a real path set (should never happen - it's only for
+		dupes). */
+		dcdebug("ShareManager::getFile: wtf, a non-renamed file has realPath set: <%s>\n", realPath.c_str());
+		return *i;
+	}
+
+	/* see if the files sorted right before this one have a real path we are looking for. this is
+	the most common case for dupes: "x (1).ext" is sorted before "x.ext". */
+	auto real = i;
+	--real;
+	while(real != d->files.end() && real->realPath) {
+		if(real->realPath == realPath) {
+			return *real;
+		}
+		--real;
+	}
+
+	/* couldn't find it before the base file? maybe it's sorted after; could happen with files with
+	no ext: "x (1)" is sorted after "x". */
+	real = i;
+	++real;
+	while(real != d->files.end() && real->realPath) {
+		if(real->realPath == realPath) {
+			return *real;
+		}
+		++real;
+	}
+
+	/* most common case: no duplicate; just return the base file. */
+	return *i;
+}
+
+void ShareManager::on(QueueManagerListener::FileMoved, const string& realPath) noexcept {
 	if(SETTING(ADD_FINISHED_INSTANTLY)) {
-		// Check if finished download is supposed to be shared
+		auto size = File::getSize(realPath);
+		if(size == -1) {
+			// looks like the file isn't actually there...
+			return;
+		}
+
 		Lock l(cs);
-		for(auto& i: shares) {
-			if(Util::strnicmp(i.first, n, i.first.size()) == 0 && n[i.first.size() - 1] == PATH_SEPARATOR) {
-				try {
-					// Schedule for hashing, it'll be added automatically later on...
-					HashManager::getInstance()->checkTTH(n, File::getSize(n), 0);
-				} catch(const Exception&) {
-					// Not a vital feature...
-				}
-				break;
-			}
+		// Check if the finished download dir is supposed to be shared
+		auto dir = getDirectory(realPath);
+		if(dir) {
+			dir->files.insert(Directory::File(Util::getFileName(realPath), size, dir,
+				HashManager::getInstance()->getTTH(realPath, size, 0)));
 		}
 	}
 }
 
-void ShareManager::on(HashManagerListener::TTHDone, const string& fname, const TTHValue& root) noexcept {
+void ShareManager::on(HashManagerListener::TTHDone, const string& realPath, const TTHValue& root) noexcept {
 	Lock l(cs);
-	Directory::Ptr d = getDirectory(fname);
-	if(d) {
-		auto i = d->findFile(Util::getFileName(fname));
-		if(i != d->files.end()) {
-			if(root != i->getTTH())
-				tthIndex.erase(i->getTTH());
-			// Get rid of false constness...
-			auto f = const_cast<Directory::File*>(&(*i));
-			f->setTTH(root);
-			tthIndex[f->getTTH()] = i;
-		} else {
-			string name = Util::getFileName(fname);
-			int64_t size = File::getSize(fname);
-			auto it = d->files.insert(Directory::File(name, size, d, root)).first;
-			updateIndices(*d, it);
-		}
+	auto f = getFile(realPath);
+	if(f) {
+		if(f->tth && root != f->tth)
+			tthIndex.erase(*f->tth);
+		const_cast<Directory::File&>(*f).tth = root;
+		tthIndex[*f->tth] = &f.get();
+
 		setDirty();
 		forceXmlRefresh = true;
 	}

=== modified file 'dcpp/ShareManager.h'
--- dcpp/ShareManager.h	2012-12-12 22:44:47 +0000
+++ dcpp/ShareManager.h	2012-12-13 17:04:31 +0000
@@ -20,6 +20,7 @@
 #define DCPLUSPLUS_DCPP_SHARE_MANAGER_H
 
 #include <list>
+#include <map>
 #include <memory>
 #include <set>
 #include <unordered_map>
@@ -45,10 +46,13 @@
 
 namespace dcpp {
 
+using std::map;
 using std::set;
 using std::unique_ptr;
 using std::unordered_map;
 
+using boost::optional;
+
 STANDARD_EXCEPTION(ShareException);
 
 class SimpleXML;
@@ -75,7 +79,7 @@
 	/** @return Actual file path & size. Returns 0 for file lists. */
 	pair<string, int64_t> toRealWithSize(const string& virtualFile);
 	StringList getRealPaths(const string& virtualPath);
-	TTHValue getTTH(const string& virtualFile) const;
+	optional<TTHValue> getTTH(const string& virtualFile) const;
 
 	void refresh(bool dirs = false, bool aUpdate = true, bool block = false) noexcept;
 	void setDirty() { xmlDirty = true; }
@@ -143,8 +147,8 @@
 			typedef set<File, FileLess> Set;
 
 			File() : size(0), parent(0) { }
-			File(const string& aName, int64_t aSize, const Directory::Ptr& aParent, const TTHValue& aRoot) :
-			name(aName), tth(aRoot), size(aSize), parent(aParent.get()) { }
+			File(const string& aName, int64_t aSize, const Directory::Ptr& aParent, const optional<TTHValue>& aRoot) :
+				name(aName), tth(aRoot), size(aSize), parent(aParent.get()) { }
 
 			bool operator==(const File& rhs) const {
 				return getParent() == rhs.getParent() && (Util::stricmp(getName(), rhs.getName()) == 0);
@@ -161,12 +165,10 @@
 			string getRealPath() const { return realPath ? realPath.get() : parent->getRealPath(name); }
 
 			GETSET(string, name, Name);
-			GETSET(TTHValue, tth, TTH);
+			optional<string> realPath; // only defined if this file had to be renamed to avoid duplication.
+			optional<TTHValue> tth;
 			GETSET(int64_t, size, Size);
 			GETSET(Directory*, parent, Parent);
-
-		private:
-			boost::optional<string> realPath;
 		};
 
 		int64_t size;
@@ -237,16 +239,15 @@
 		int64_t gt;
 		int64_t lt;
 
-		TTHValue root;
-		bool hasRoot;
+		optional<TTHValue> root;
 
 		bool isDirectory;
 	};
 
 	int64_t xmlListLen;
-	TTHValue xmlRoot;
+	optional<TTHValue> xmlRoot;
 	int64_t bzXmlListLen;
-	TTHValue bzXmlRoot;
+	optional<TTHValue> bzXmlRoot;
 	unique_ptr<File> bzXmlRef;
 
 	bool xmlDirty;
@@ -272,14 +273,11 @@
 	The map is sorted to make sure conflicts are always resolved in the same order when merging. */
 	map<string, string> shares;
 
-	typedef unordered_map<TTHValue, Directory::File::Set::const_iterator> HashFileMap;
-	typedef HashFileMap::iterator HashFileIter;
-
-	HashFileMap tthIndex;
+	unordered_map<TTHValue, const Directory::File*> tthIndex;
 
 	BloomFilter<5> bloom;
 
-	Directory::File::Set::const_iterator findFile(const string& virtualFile) const;
+	const Directory::File& findFile(const string& virtualFile) const;
 
 	Directory::Ptr buildTree(const string& aName, const Directory::Ptr& aParent);
 	bool checkHidden(const string& aName) const;
@@ -297,15 +295,19 @@
 	pair<Directory::Ptr, string> splitVirtual(const string& virtualPath) const;
 	string findRealRoot(const string& virtualRoot, const string& virtualLeaf) const;
 
-	Directory::Ptr getDirectory(const string& fname);
+	/** Get the directory pointer corresponding to a given real path (on disk). Note that only
+	directories are considered here but not the file's base name. */
+	Directory::Ptr getDirectory(const string& realPath) noexcept;
+	/** Get the file corresponding to a given real path (on disk). */
+	optional<const ShareManager::Directory::File&> getFile(const string& realPath, Directory::Ptr d = nullptr) noexcept;
 
 	virtual int run();
 
 	// QueueManagerListener
-	virtual void on(QueueManagerListener::FileMoved, const string& n) noexcept;
+	virtual void on(QueueManagerListener::FileMoved, const string& realPath) noexcept;
 
 	// HashManagerListener
-	virtual void on(HashManagerListener::TTHDone, const string& fname, const TTHValue& root) noexcept;
+	virtual void on(HashManagerListener::TTHDone, const string& realPath, const TTHValue& root) noexcept;
 
 	// SettingsManagerListener
 	virtual void on(SettingsManagerListener::Save, SimpleXML& xml) noexcept {