/* */ #include "DHTBucket.h" #include #include #include #include "DHTNode.h" #include "LogFactory.h" #include "Logger.h" #include "util.h" #include "DHTConstants.h" #include "a2functional.h" #include "bittorrent_helper.h" #include "bitfield.h" #include "wallclock.h" namespace aria2 { DHTBucket::DHTBucket(size_t prefixLength, const unsigned char* max, const unsigned char* min, const SharedHandle& localNode): prefixLength_(prefixLength), localNode_(localNode), lastUpdated_(global::wallclock), logger_(LogFactory::getInstance()) { memcpy(max_, max, DHT_ID_LENGTH); memcpy(min_, min, DHT_ID_LENGTH); } DHTBucket::DHTBucket(const SharedHandle& localNode): prefixLength_(0), localNode_(localNode), lastUpdated_(global::wallclock), logger_(LogFactory::getInstance()) { memset(max_, 0xffu, DHT_ID_LENGTH); memset(min_, 0, DHT_ID_LENGTH); } DHTBucket::~DHTBucket() {} void DHTBucket::getRandomNodeID(unsigned char* nodeID) const { if(prefixLength_ == 0) { util::generateRandomKey(nodeID); } else { size_t lastByteIndex = (prefixLength_-1)/8; util::generateRandomKey(nodeID); memcpy(nodeID, min_, lastByteIndex+1); } } bool DHTBucket::isInRange(const SharedHandle& node) const { return isInRange(node->getID(), max_, min_); } bool DHTBucket::isInRange(const unsigned char* nodeID) const { return isInRange(nodeID, max_, min_); } // Returns true if nodeID is in [min, max] (inclusive). bool DHTBucket::isInRange(const unsigned char* nodeID, const unsigned char* max, const unsigned char* min) const { return !std::lexicographical_compare(&nodeID[0], &nodeID[DHT_ID_LENGTH], &min[0], &min[DHT_ID_LENGTH]) && !std::lexicographical_compare(&max[0], &max[DHT_ID_LENGTH], &nodeID[0], &nodeID[DHT_ID_LENGTH]); } 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()); unsigned char rMax[DHT_ID_LENGTH]; memcpy(rMax, max_, DHT_ID_LENGTH); bitfield::flipBit(rMax, DHT_ID_LENGTH, prefixLength_); unsigned char rMin[DHT_ID_LENGTH]; memcpy(rMin, min_, DHT_ID_LENGTH); bitfield::flipBit(min_, DHT_ID_LENGTH, prefixLength_); ++prefixLength_; SharedHandle rBucket(new DHTBucket(prefixLength_, rMax, rMin, localNode_)); std::deque > lNodes; for(std::deque >::iterator i = nodes_.begin(), eoi = nodes_.end(); i != eoi; ++i) { if(rBucket->isInRange(*i)) { assert(rBucket->addNode(*i)); } else { lNodes.push_back(*i); } } nodes_ = lNodes; // TODO create toString() and use it. if(logger_->debug()) { logger_->debug("New bucket. prefixLength=%u, Range:%s-%s", static_cast(rBucket->getPrefixLength()), util::toHex(rBucket->getMinID(), DHT_ID_LENGTH).c_str(), util::toHex(rBucket->getMaxID(), DHT_ID_LENGTH).c_str()); logger_->debug("Existing bucket. prefixLength=%u, Range:%s-%s", static_cast(prefixLength_), util::toHex(getMinID(), DHT_ID_LENGTH).c_str(), util::toHex(getMaxID(), DHT_ID_LENGTH).c_str()); } return rBucket; } void DHTBucket::getGoodNodes (std::vector >& goodNodes) const { goodNodes.insert(goodNodes.end(), nodes_.begin(), nodes_.end()); goodNodes.erase(std::remove_if(goodNodes.begin(), goodNodes.end(), mem_fun_sh(&DHTNode::isBad)), goodNodes.end()); } 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_.difference(global::wallclock) >= DHT_BUCKET_REFRESH_INTERVAL; } void DHTBucket::notifyUpdate() { lastUpdated_ = global::wallclock; } namespace { class FindQuestionableNode { public: bool operator()(const SharedHandle& node) const { return node->isQuestionable(); } }; } // namespace 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; } } } // namespace aria2