📄 hashmanager.cpp
字号:
/*
* Copyright (C) 2001-2006 Jacek Sieka, arnetheduck on gmail point com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stdinc.h"
#include "DCPlusPlus.h"
#include "HashManager.h"
#include "ResourceManager.h"
#include "SimpleXML.h"
#include "LogManager.h"
#include "File.h"
#include "ZUtils.h"
#include "SFVReader.h"
#define HASH_FILE_VERSION_STRING "2"
static const u_int32_t HASH_FILE_VERSION=2;
const int64_t HashManager::MIN_BLOCK_SIZE = 64*1024;
bool HashManager::checkTTH(const string& aFileName, int64_t aSize, u_int32_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) throw(HashException) {
Lock l(cs);
const TTHValue* tth = store.getTTH(aFileName);
if(tth == NULL){
hasher.hashFile(aFileName, aSize);
throw HashException(Util::emptyString);
}
return *tth;
}
bool HashManager::getTree(const TTHValue& root, TigerTree& tt) {
Lock l(cs);
return store.getTree(root, tt);
}
void HashManager::hashDone(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tth, int64_t speed) {
try {
Lock l(cs);
store.addFile(aFileName, aTimeStamp, tth, true);
} catch(const Exception& e) {
LogManager::getInstance()->message(STRING(HASHING_FAILED) + e.getError());
return;
}
fire(HashManagerListener::TTHDone(), aFileName, tth.getRoot());
string fn = aFileName;
if(count(fn.begin(), fn.end(), PATH_SEPARATOR) >= 2) {
string::size_type i = fn.rfind(PATH_SEPARATOR);
i = fn.rfind(PATH_SEPARATOR, i-1);
fn.erase(0, i);
fn.insert(0, "...");
}
if(speed > 0) {
LogManager::getInstance()->message(STRING(HASHING_FINISHED) + fn + " (" + Util::formatBytes(speed) + "/s)");
} else {
LogManager::getInstance()->message(STRING(HASHING_FINISHED) + fn);
}
}
void HashManager::HashStore::addFile(const string& aFileName, u_int32_t aTimeStamp, const TigerTree& tth, bool aUsed) {
addTree(tth);
string fname = Text::toLower(Util::getFileName(aFileName));
string fpath = Text::toLower(Util::getFilePath(aFileName));
FileInfoList& fileList = fileIndex[fpath];
FileInfoIter j = find(fileList.begin(), fileList.end(), fname);
if(j != fileList.end()) {
fileList.erase(j);
}
fileList.push_back(FileInfo(fname, tth.getRoot(), aTimeStamp, aUsed));
dirty = true;
}
void HashManager::HashStore::addTree(const TigerTree& tt) {
if(treeIndex.find(tt.getRoot()) == treeIndex.end()) {
File f(getDataFile(), File::READ|File::WRITE, File::OPEN);
int64_t index = saveTree(f, tt);
treeIndex.insert(make_pair(tt.getRoot(), TreeInfo(tt.getFileSize(), index, tt.getBlockSize())));
dirty = true;
}
}
int64_t HashManager::HashStore::saveTree(File& f, const TigerTree& tt) {
if(tt.getLeaves().size() == 1)
return SMALL_TREE;
f.setPos(0);
int64_t pos = 0;
size_t n = sizeof(pos);
if(f.read(&pos, n) != sizeof(pos))
throw HashException(STRING(HASH_READ_FAILED));
// Check if we should grow the file, we grow by a meg at a time...
int64_t datsz = f.getSize();
if((pos + (int64_t)(tt.getLeaves().size() * TTHValue::SIZE)) >= datsz) {
f.setPos(datsz + 1024*1024);
f.setEOF();
}
f.setPos(pos);
dcassert(tt.getLeaves().size() > 1);
f.write(tt.getLeaves()[0].data, (tt.getLeaves().size() * TTHValue::SIZE));
int64_t p2 = f.getPos();
f.setPos(0);
f.write(&p2, sizeof(p2));
return pos;
}
bool HashManager::HashStore::loadTree(File& f, const TreeInfo& ti, const TTHValue& root, TigerTree& tt) {
if(ti.getIndex() == SMALL_TREE) {
tt = TigerTree(ti.getSize(), ti.getBlockSize(), root);
return true;
}
try {
f.setPos(ti.getIndex());
size_t datalen = TigerTree::calcBlocks(ti.getSize(), ti.getBlockSize()) * TTHValue::SIZE;
AutoArray<u_int8_t> buf(datalen);
f.read((u_int8_t*)buf, datalen);
tt = TigerTree(ti.getSize(), ti.getBlockSize(), buf);
if(!(tt.getRoot() == root))
return false;
} catch(const Exception&) {
return false;
}
return true;
}
bool HashManager::HashStore::getTree(const TTHValue& root, TigerTree& tt) {
TreeIter i = treeIndex.find(root);
if(i == treeIndex.end())
return false;
try {
File f(getDataFile(), File::READ, File::OPEN);
return loadTree(f, i->second, root, tt);
} catch(const Exception&) {
return false;
}
}
bool HashManager::HashStore::checkTTH(const string& aFileName, int64_t aSize, u_int32_t aTimeStamp) {
string fname = Text::toLower(Util::getFileName(aFileName));
string fpath = Text::toLower(Util::getFilePath(aFileName));
DirIter i = fileIndex.find(fpath);
if(i != fileIndex.end()) {
FileInfoIter j = find(i->second.begin(), i->second.end(), fname);
if(j != i->second.end()) {
FileInfo& fi = *j;
TreeIter ti = treeIndex.find(fi.getRoot());
if(ti == treeIndex.end() || ti->second.getSize() != aSize || fi.getTimeStamp() != aTimeStamp) {
i->second.erase(j);
dirty = true;
return false;
}
return true;
}
}
return false;
}
const TTHValue* HashManager::HashStore::getTTH(const string& aFileName) {
string fname = Text::toLower(Util::getFileName(aFileName));
string fpath = Text::toLower(Util::getFilePath(aFileName));
DirIter i = fileIndex.find(fpath);
if(i != fileIndex.end()) {
FileInfoIter j = find(i->second.begin(), i->second.end(), fname);
if(j != i->second.end()) {
j->setUsed(true);
return &(j->getRoot());
}
}
return NULL;
}
void HashManager::HashStore::rebuild() {
try {
DirMap newFileIndex;
TreeMap newTreeIndex;
for(DirIter i = fileIndex.begin(); i != fileIndex.end(); ++i) {
for(FileInfoIter j = i->second.begin(); j != i->second.end(); ++j) {
if(!j->getUsed())
continue;
TreeIter k = treeIndex.find(j->getRoot());
if(k != treeIndex.end()) {
newTreeIndex[j->getRoot()] = k->second;
}
}
}
string tmpName = getDataFile() + ".tmp";
string origName = getDataFile();
createDataFile(tmpName);
{
File in(origName, File::READ, File::OPEN);
File out(tmpName, File::READ|File::WRITE, File::OPEN);
for(TreeIter i = newTreeIndex.begin(); i != newTreeIndex.end();) {
TigerTree tree;
if(loadTree(in, i->second, i->first,tree)) {
i->second.setIndex(saveTree(out, tree));
++i;
} else {
newTreeIndex.erase(i++);
}
}
}
for(DirIter i = fileIndex.begin(); i != fileIndex.end(); ++i) {
DirIter fi = newFileIndex.insert(make_pair(i->first, FileInfoList())).first;
for(FileInfoIter j = i->second.begin(); j != i->second.end(); ++j) {
if(newTreeIndex.find(j->getRoot()) != newTreeIndex.end()) {
fi->second.push_back(*j);
}
}
if(fi->second.empty())
newFileIndex.erase(fi);
}
File::deleteFile(origName);
File::renameFile(tmpName, origName);
treeIndex = newTreeIndex;
fileIndex = newFileIndex;
dirty = true;
save();
} catch(const Exception& e) {
LogManager::getInstance()->message(STRING(HASHING_FAILED) + e.getError());
}
}
void HashManager::HashStore::save() {
if(dirty) {
try {
File ff(getIndexFile() + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
BufferedOutputStream<false> f(&ff);
string tmp;
string b32tmp;
f.write(SimpleXML::utf8Header);
f.write(LIT("<HashStore Version=\"" HASH_FILE_VERSION_STRING "\">\r\n"));
f.write(LIT("\t<Trees>\r\n"));
for(TreeIter i = treeIndex.begin(); i != treeIndex.end(); ++i) {
const TreeInfo& ti = i->second;
f.write(LIT("\t\t<Hash Type=\"TTH\" Index=\""));
f.write(Util::toString(ti.getIndex()));
f.write(LIT("\" BlockSize=\""));
f.write(Util::toString(ti.getBlockSize()));
f.write(LIT("\" Size=\""));
f.write(Util::toString(ti.getSize()));
f.write(LIT("\" Root=\""));
b32tmp.clear();
f.write(i->first.toBase32(b32tmp));
f.write(LIT("\"/>\r\n"));
}
f.write(LIT("\t</Trees>\r\n\t<Files>\r\n"));
for(DirIter i = fileIndex.begin(); i != fileIndex.end(); ++i) {
const string& dir = i->first;
for(FileInfoIter j = i->second.begin(); j != i->second.end(); ++j) {
const FileInfo& fi = *j;
f.write(LIT("\t\t<File Name=\""));
f.write(SimpleXML::escape(dir + fi.getFileName(), tmp, true));
f.write(LIT("\" TimeStamp=\""));
f.write(Util::toString(fi.getTimeStamp()));
f.write(LIT("\" Root=\""));
b32tmp.clear();
f.write(fi.getRoot().toBase32(b32tmp));
f.write(LIT("\"/>\r\n"));
}
}
f.write(LIT("\t</Files>\r\n</HashStore>"));
f.flush();
ff.close();
File::deleteFile(getIndexFile());
File::renameFile(getIndexFile() + ".tmp", getIndexFile());
dirty = false;
} catch(const FileException&) {
// Too bad...
}
}
}
class HashLoader : public SimpleXMLReader::CallBack {
public:
HashLoader(HashManager::HashStore& s) : store(s), size(0), timeStamp(0), version(HASH_FILE_VERSION), inTrees(false), inFiles(false), inHashStore(false) { }
virtual void startTag(const string& name, StringPairList& attribs, bool simple);
virtual void endTag(const string& name, const string& data);
private:
HashManager::HashStore& store;
string file;
int64_t size;
u_int32_t timeStamp;
int version;
bool inTrees;
bool inFiles;
bool inHashStore;
};
void HashManager::HashStore::load() {
try {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -