diff --git a/include/opendht/indexation/pht.h b/include/opendht/indexation/pht.h index 05ca637f3e32de2b00739471b6eb67b6b973bfa0..d60afbe1f903d7045137ececf15a4e5fab7315a8 100644 --- a/include/opendht/indexation/pht.h +++ b/include/opendht/indexation/pht.h @@ -142,6 +142,9 @@ struct IndexEntry : public dht::Value::Serializable<IndexEntry> { }; class Pht { + static constexpr const char* INVALID_KEY = "Key does not match the PHT key spec."; + + /* Prefixes the user_type for all dht values put on the DHT */ static constexpr const char* INDEX_PREFIX = "index.pht."; public: @@ -150,7 +153,11 @@ public: */ static constexpr const size_t MAX_NODE_ENTRY_COUNT {16}; + /* A key for a an index entry */ using Key = std::map<std::string, Blob>; + /* Specifications of the keys. It defines the number, the length and the + * serialization order of fields. */ + using KeySpec = std::map<std::string, size_t>; using LookupCallback = std::function<void(std::vector<std::shared_ptr<Value>>& values, Prefix p)>; typedef void (*LookupCallbackRaw)(std::vector<std::shared_ptr<Value>>* values, Prefix* p, void *user_data); @@ -162,8 +169,12 @@ public: }; } - Pht(std::string name, std::shared_ptr<DhtRunner> dht) - : name_(INDEX_PREFIX + name), canary_(name_ + ".canary"), dht_(dht) { } + Pht(std::string name, KeySpec k_spec, std::shared_ptr<DhtRunner> dht) + : name_(INDEX_PREFIX + name), canary_(name_ + ".canary"), keySpec_(k_spec), dht_(dht) + { + if (k_spec.size() != 1) + throw std::invalid_argument("PHT only supports unidimensional data."); + } virtual ~Pht () { } /** @@ -295,27 +306,39 @@ private: std::multimap<time_point, std::shared_ptr<Node>> leaves_; }; + /** + * 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, + DoneCallbackSimple done_cb, std::shared_ptr<unsigned> max_common_prefix_len, + int start = -1, bool all_values = false); + /** * Linearizes the key into a unidimensional key. A pht only takes * unidimensional key. * * @param Key The initial key. * - * @return return The linearized key. + * @return the prefix of the linearized key. */ - static Prefix linearize(Key k) { - if (k.size() != 1) { throw std::invalid_argument("PHT only supports unidimensional data."); } - return k.begin()->second; + virtual Prefix linearize(Key k) const { + if (not validKey(k)) { throw std::invalid_argument(INVALID_KEY); } + return Blob {k.begin()->second.begin(), k.begin()->second.begin() + keySpec_.begin()->second}; }; /** - * Performs a step in the lookup operation. Each steps are performed - * asynchronously. + * Tells if the key is valid according to the key spec. */ - 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, - DoneCallbackSimple done_cb, std::shared_ptr<unsigned> max_common_prefix_len, - int start = -1, bool all_values = false); + bool validKey(const Key& k) const { + return k.size() == keySpec_.size() and + std::equal(k.begin(), k.end(), keySpec_.begin(), + [&](const Key::value_type& key, const KeySpec::value_type& key_spec) { + return key.first == key_spec.first; + } + ); + } /** * Updates the canary token on the node responsible for the specified @@ -325,6 +348,7 @@ private: const std::string name_; const std::string canary_; + const KeySpec keySpec_; Cache cache_; std::shared_ptr<DhtRunner> dht_; }; diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 2dabb2cbca6fc9545d8d86f87cf7abc1b829634e..f8a6613bc3d346bbbeba27c98815911b6ab3b192 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -73,7 +73,7 @@ void print_help() { << std::endl; } -void cmd_loop(std::shared_ptr<DhtRunner>& dht, std::map<std::string, dht::indexation::Pht> indexes, dht_params& params) +void cmd_loop(std::shared_ptr<DhtRunner>& dht, std::map<std::string, indexation::Pht> indexes, dht_params& params) { print_node_info(dht, params); std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl; @@ -161,11 +161,23 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, std::map<std::string, dht::indexa if (op == "il" or op == "ii") { // Pht syntax iss >> index >> keystr; + auto new_index = std::find_if(indexes.begin(), indexes.end(), + [&](std::pair<const std::string, indexation::Pht>& i) { + return i.first == index; + }) == indexes.end(); if (not index.size()) { std::cout << "You must enter the index name." << std::endl; continue; - } else { - indexes.emplace(index, dht::indexation::Pht {index, dht}); + } else if (new_index) { + using namespace dht::indexation; + try { + auto key = createPhtKey(parseStringMap(keystr)); + Pht::KeySpec ks; + std::transform(key.begin(), key.end(), std::inserter(ks, ks.end()), [](Pht::Key::value_type& f) { + return std::make_pair(f.first, f.second.size()); + }); + indexes.emplace(index, Pht {index, std::move(ks), dht}); + } catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; } } } else { @@ -265,25 +277,30 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, std::map<std::string, dht::indexa else if (op == "il") { std::string exact_match; iss >> exact_match; - indexes.at(index).lookup(createPhtKey(parseStringMap(keystr)), - [=](std::vector<std::shared_ptr<indexation::Value>>& vals, indexation::Prefix p) { - std::cout << "Pht::lookup: at prefix \"" << p.toString() << "\"" << ", hash is " << p.hash() << std::endl; - for (auto v : vals) { - std::cout << "Pht::lookup: found hash" << std::endl - << "\t" << v->first << "[vid: " << v->second << "]" << std::endl; - } - std::cout << "Pht::lookup: done." << std::endl; - }, - [start](bool ok) { - if (not ok) { - std::cout << "Pht::lookup: dht Get failed." << std::endl; - } - - auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Pht::lookup: took " << print_dt(end-start) << "s)" << std::endl; - - }, exact_match.size() != 0 and exact_match == "false" ? false : true - ); + try { + auto key = createPhtKey(parseStringMap(keystr)); + std::cout << "Pht::lookup(key=\"" << indexes.at(index).linearize(key).toString() << "\")" << std::endl; + indexes.at(index).lookup(key, + [=](std::vector<std::shared_ptr<indexation::Value>>& vals, indexation::Prefix p) { + if (vals.empty()) + return; + std::cout << "Pht::lookup: found entries!" << std::endl + << " prefix: \"" << p.toString() << "\"" << std::endl + << " hash: " << p.hash() << std::endl; + std::cout << " entries:" << std::endl; + for (auto v : vals) + std::cout << " " << v->first.toString() << "[vid: " << v->second << "]" << std::endl; + }, + [start](bool ok) { + auto end = std::chrono::high_resolution_clock::now(); + std::cout << "Pht::lookup: " << (ok ? "done." : "failed.") + << " took " << print_dt(end-start) << "s)" << std::endl; + + }, exact_match.size() != 0 and exact_match == "false" ? false : true + ); + } + catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; } + catch (std::out_of_range& e) { } } else if (op == "ii") { iss >> idstr; @@ -292,15 +309,22 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, std::map<std::string, dht::indexa continue; indexation::Value v {h, 0}; - indexes.at(index).insert(createPhtKey(parseStringMap(keystr)), v, - [=](bool success) { - if (not success) { - std::cout << "Pht::insert: failed." << std::endl; - return; + try { + auto key = createPhtKey(parseStringMap(keystr)); + std::cout << "Pht::insert(key=\"" << indexes.at(index).linearize(key).toString() + << "\", " << h.toString() << ")" << std::endl; + indexes.at(index).insert(key, v, + [=](bool success) { + if (not success) { + std::cout << "Pht::insert: failed." << std::endl; + return; + } + std::cout << "Pht::insert: done." << std::endl; } - std::cout << "Pht::insert: done." << std::endl; - } - ); + ); + } + catch (std::invalid_argument& e) { std::cout << e.what() << std::endl; } + catch (std::out_of_range& e) { } } } @@ -314,7 +338,7 @@ main(int argc, char **argv) throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc)); auto dht = std::make_shared<DhtRunner>(); - std::map<std::string, dht::indexation::Pht> indexes; + std::map<std::string, indexation::Pht> indexes; try { auto params = parseArgs(argc, argv);