/* */ #include "DHTBucket.h" #include "DHTUtil.h" #include "DHTNode.h" #include "LogFactory.h" #include "Logger.h" #include "Util.h" #include "DHTConstants.h" #include "a2functional.h" #include #include #include namespace aria2 { DHTBucket::DHTBucket(size_t prefixLength, const unsigned char* max, const unsigned char* min, const SharedHandle& localNode): _prefixLength(prefixLength), _localNode(localNode), _logger(LogFactory::getInstance()) { memcpy(_max, max, DHT_ID_LENGTH); memcpy(_min, min, DHT_ID_LENGTH); } DHTBucket::DHTBucket(const SharedHandle& localNode): _prefixLength(0), _localNode(localNode), _logger(LogFactory::getInstance()) { memset(_max, 0xff, DHT_ID_LENGTH); memset(_min, 0, DHT_ID_LENGTH); } DHTBucket::~DHTBucket() {} void DHTBucket::getRandomNodeID(unsigned char* nodeID) const { if(_prefixLength == 0) { DHTUtil::generateRandomKey(nodeID); } else { size_t lastByteIndex = (_prefixLength-1)/8; DHTUtil::generateRandomKey(nodeID); memcpy(nodeID, _min, lastByteIndex+1); } } bool DHTBucket::isInRange(const SharedHandle& node) const { return isInRange(node->getID()); } bool DHTBucket::isInRange(const unsigned char* nodeID) const { for(size_t i = 0; i < DHT_ID_LENGTH; ++i) { if(nodeID[i] < _min[i]) { return false; } else if(_min[i] < nodeID[i]) { break; } } for(size_t i = 0; i < DHT_ID_LENGTH; ++i) { if(_max[i] < nodeID[i]) { return false; } else if(nodeID[i] < _max[i]) { break; } } return true; } bool DHTBucket::addNode(const SharedHandle& node) { notifyUpdate(); std::deque >::iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr == _nodes.end()) { if(_nodes.size() < K) { _nodes.push_back(node); return true; } else { if(_nodes.front()->isBad()) { _nodes.erase(_nodes.begin()); _nodes.push_back(node); return true; } else { return false; } } } else { _nodes.erase(itr); _nodes.push_back(node); return true; } } void DHTBucket::cacheNode(const SharedHandle& node) { // _cachedNodes are sorted by last time seen _cachedNodes.push_front(node); if(_cachedNodes.size() > CACHE_SIZE) { _cachedNodes.resize(CACHE_SIZE, SharedHandle()); } } void DHTBucket::dropNode(const SharedHandle& node) { if(_cachedNodes.size()) { std::deque >::iterator itr = find(_nodes.begin(), _nodes.end(), node); if(itr != _nodes.end()) { _nodes.erase(itr); _nodes.push_back(_cachedNodes.front()); _cachedNodes.erase(_cachedNodes.begin()); } } } void DHTBucket::moveToHead(const SharedHandle& node) { std::deque >::iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr != _nodes.end()) { _nodes.erase(itr); _nodes.push_front(node); } } void DHTBucket::moveToTail(const SharedHandle& node) { std::deque >::iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr != _nodes.end()) { _nodes.erase(itr); _nodes.push_back(node); } } bool DHTBucket::splitAllowed() const { return _prefixLength < DHT_ID_LENGTH*8-1 && isInRange(_localNode); } SharedHandle DHTBucket::split() { assert(splitAllowed()); size_t newPrefixLength = _prefixLength+1; unsigned char rMax[DHT_ID_LENGTH]; memcpy(rMax, _max, DHT_ID_LENGTH); DHTUtil::flipBit(rMax, DHT_ID_LENGTH, _prefixLength); SharedHandle rBucket(new DHTBucket(newPrefixLength, rMax, _min, _localNode)); std::deque > tempNodes = _nodes; for(std::deque >::iterator i = tempNodes.begin(); i != tempNodes.end();) { if(rBucket->isInRange(*i)) { assert(rBucket->addNode(*i)); i = tempNodes.erase(i); } else { ++i; } } DHTUtil::flipBit(_min, DHT_ID_LENGTH, _prefixLength); _prefixLength = newPrefixLength; _nodes = tempNodes; // TODO create toString() and use it. _logger->debug("New bucket. Range:%s-%s", Util::toHex(rBucket->getMinID(), DHT_ID_LENGTH).c_str(), Util::toHex(rBucket->getMaxID(), DHT_ID_LENGTH).c_str()); _logger->debug("Existing bucket. Range:%s-%s", Util::toHex(getMinID(), DHT_ID_LENGTH).c_str(), Util::toHex(getMaxID(), DHT_ID_LENGTH).c_str()); return rBucket; } size_t DHTBucket::countNode() const { return _nodes.size(); } const std::deque >& DHTBucket::getNodes() const { return _nodes; } std::deque > DHTBucket::getGoodNodes() const { std::deque > goodNodes = _nodes; goodNodes.erase(std::remove_if(goodNodes.begin(), goodNodes.end(), mem_fun_sh(&DHTNode::isBad)), goodNodes.end()); return goodNodes; } SharedHandle DHTBucket::getNode(const unsigned char* nodeID, const std::string& ipaddr, uint16_t port) const { SharedHandle node(new DHTNode(nodeID)); node->setIPAddress(ipaddr); node->setPort(port); std::deque >::const_iterator itr = std::find(_nodes.begin(), _nodes.end(), node); if(itr == _nodes.end()) { return SharedHandle(); } else { return *itr; } } bool DHTBucket::operator==(const DHTBucket& bucket) const { return memcmp(_max, bucket._max, DHT_ID_LENGTH) == 0 && memcmp(_min, bucket._min, DHT_ID_LENGTH) == 0; } bool DHTBucket::needsRefresh() const { return _nodes.size() < K || _lastUpdated.elapsed(DHT_BUCKET_REFRESH_INTERVAL); } void DHTBucket::notifyUpdate() { _lastUpdated.reset(); } class FindQuestionableNode { public: bool operator()(const SharedHandle& node) const { return node->isQuestionable(); } }; bool DHTBucket::containsQuestionableNode() const { return std::find_if(_nodes.begin(), _nodes.end(), FindQuestionableNode()) != _nodes.end(); } SharedHandle DHTBucket::getLRUQuestionableNode() const { std::deque >::const_iterator i = std::find_if(_nodes.begin(), _nodes.end(), FindQuestionableNode()); if(i == _nodes.end()) { return SharedHandle(); } else { return *i; } } const std::deque >& DHTBucket::getCachedNodes() const { return _cachedNodes; } } // namespace aria2