diff --git a/include/opendht/indexation/pht.h b/include/opendht/indexation/pht.h index 5e91ac9cdb35628e2cc38d23b4d97993724140da..45e7d9cc8098bf4c8f9e95e9555551328fd4d71c 100644 --- a/include/opendht/indexation/pht.h +++ b/include/opendht/indexation/pht.h @@ -15,6 +15,15 @@ namespace dht { namespace indexation { +/*! + * @class Prefix + * @brief A blob structure which prefixes a Key in the PHT. + * @details + * Since the PHT structure is a "trie", every node in this structure have a + * label which is defined by the path from the root of the trie to the node. If + * the node in question is a leaf, *the label is a prefix of all the keys + * contained in the leaf*. + */ struct Prefix { Prefix() {} Prefix(InfoHash h) : size_(h.size() * 8), content_(h.begin(), h.end()) {} @@ -32,6 +41,12 @@ struct Prefix { len += size_; return Prefix(*this, len); } + + /** + * This methods gets the prefix of its sibling in the PHT structure. + * + * @return The prefix of this sibling. + */ Prefix getSibling() const { Prefix copy = *this; if (size_) { @@ -59,6 +74,30 @@ struct Prefix { return ss.str(); } + static inline unsigned commonBits(const Prefix& p1, const Prefix& p2) { + unsigned i, j; + uint8_t x; + auto longest_prefix_size = std::min(p1.size_, p2.size_); + + for (i = 0; i < longest_prefix_size; i++) { + if (p1.content_.data()[i] != p2.content_.data()[i]) + break; + } + + if (i == longest_prefix_size) + return 8*longest_prefix_size; + + x = p1.content_.data()[i] ^ p2.content_.data()[i]; + + j = 0; + while ((x & 0x80) == 0) { + x <<= 1; + j++; + } + + return 8 * i + j; + } + size_t size_ {0}; Blob content_ {}; }; @@ -111,7 +150,7 @@ public: /** * Lookup a key for a value. */ - void lookup(Key k, LookupCallback cb = {}, Dht::DoneCallbackSimple doneCb = {}); + void lookup(Key k, LookupCallback cb = {}, Dht::DoneCallbackSimple doneCb = {}, bool exact_match = true); /** * Adds an entry into the index. */ @@ -135,10 +174,10 @@ private: * Performs a step in the lookup operation. Each steps are performed * asynchronously. */ - void lookupStep(Prefix k, std::shared_ptr<int> lo, - std::shared_ptr<int> hi, - std::shared_ptr<std::vector<std::shared_ptr<Value>>> vals, - LookupCallback cb, Dht::DoneCallbackSimple done_cb); + void lookupStep(Prefix k, std::shared_ptr<int> lo, std::shared_ptr<int> hi, + std::shared_ptr<std::vector<std::shared_ptr<Value>>> vals, + LookupCallback cb, Dht::DoneCallbackSimple done_cb, + std::shared_ptr<unsigned> max_common_prefix_len); /** * Updates the canary token on the node responsible for the specified diff --git a/src/indexation/pht.cpp b/src/indexation/pht.cpp index 07442a6781ac72cb7e5a70aba515c5ecd88c8df1..1b2e2636a95a6311487d780621213f22742ffedf 100644 --- a/src/indexation/pht.cpp +++ b/src/indexation/pht.cpp @@ -4,10 +4,12 @@ namespace dht { namespace indexation { -void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, - std::shared_ptr<int> hi, - std::shared_ptr<std::vector<std::shared_ptr<Value>>> vals, - LookupCallback cb, Dht::DoneCallbackSimple done_cb) +const ValueType IndexEntry::TYPE = ValueType::USER_DATA; + +void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, std::shared_ptr<int> hi, + std::shared_ptr<std::vector<std::shared_ptr<Value>>> vals, + LookupCallback cb, Dht::DoneCallbackSimple done_cb, + std::shared_ptr<unsigned> max_common_prefix_len) { struct node_lookup_result { bool done {false}; @@ -28,7 +30,7 @@ void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, } else { // internal node *lo = mid+1; - lookupStep(p, lo, hi, vals, cb, done_cb); + lookupStep(p, lo, hi, vals, cb, done_cb, max_common_prefix_len); } }; if (*lo <= *hi) { @@ -42,8 +44,27 @@ void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, else { IndexEntry entry; entry.unpackValue(*value); - if (entry.prefix == p.content_) + + auto add_value = [&](bool better = true) { vals->emplace_back(std::make_shared<Value>(entry.value)); + if (better and max_common_prefix_len) + *max_common_prefix_len = Prefix::commonBits(p, vals->front()->first); + }; + if (max_common_prefix_len) { + if (vals->empty()) { + add_value(); + } else { + auto common_bits = Prefix::commonBits(vals->front()->first, p.getPrefix(mid)); + if (common_bits == *max_common_prefix_len) + add_value(false); + else if (common_bits > *max_common_prefix_len) { + vals->clear(); + add_value(); + } + } + } + else if (entry.prefix == p.content_) + add_value(false); } return true; }; @@ -59,7 +80,7 @@ void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, if (not first_res->is_pht) { // Not a PHT node. *hi = mid-1; - lookupStep(p, lo, hi, vals, cb, done_cb); + lookupStep(p, lo, hi, vals, cb, done_cb, max_common_prefix_len); } else { first_res->done = true; if (second_res->done) @@ -88,12 +109,13 @@ void Pht::lookupStep(Prefix p, std::shared_ptr<int> lo, } } -void Pht::lookup(Key k, Pht::LookupCallback cb, Dht::DoneCallbackSimple done_cb) { +void Pht::lookup(Key k, Pht::LookupCallback cb, Dht::DoneCallbackSimple done_cb, bool exact_match) { auto values = std::make_shared<std::vector<std::shared_ptr<Value>>>(); auto prefix = linearize(k); auto lo = std::make_shared<int>(0); auto hi = std::make_shared<int>(prefix.size_); - lookupStep(prefix, lo, hi, values, cb, done_cb); + std::shared_ptr<unsigned> max_common_prefix_len = not exact_match ? std::make_shared<unsigned>(0) : nullptr; + lookupStep(prefix, lo, hi, values, cb, done_cb, max_common_prefix_len); } void Pht::updateCanary(Prefix p) { @@ -145,7 +167,7 @@ void Pht::insert(Key k, Value v, Dht::DoneCallbackSimple done_cb) { updateCanary(*final_prefix); dht_->put(final_prefix->hash(), std::move(entry), done_cb); } - } + }, nullptr ); }