diff --git a/node/Dictionary.cpp b/node/Dictionary.cpp index fb49002a5..578fdedc4 100644 --- a/node/Dictionary.cpp +++ b/node/Dictionary.cpp @@ -32,6 +32,68 @@ namespace ZeroTier { +Dictionary::iterator Dictionary::find(const std::string &key) +{ + for(iterator i(begin());i!=end();++i) { + if (i->first == key) + return i; + } + return end(); +} +Dictionary::const_iterator Dictionary::find(const std::string &key) const +{ + for(const_iterator i(begin());i!=end();++i) { + if (i->first == key) + return i; + } + return end(); +} + +bool Dictionary::getBoolean(const std::string &key,bool dfl) const +{ + const_iterator e(find(key)); + if (e == end()) + return dfl; + if (e->second.length() < 1) + return dfl; + switch(e->second[0]) { + case '1': + case 't': + case 'T': + case 'y': + case 'Y': + return true; + } + return false; +} + +std::string &Dictionary::operator[](const std::string &key) +{ + for(iterator i(begin());i!=end();++i) { + if (i->first == key) + return i->second; + } + push_back(std::pair(key,std::string())); + std::sort(begin(),end()); + for(iterator i(begin());i!=end();++i) { + if (i->first == key) + return i->second; + } + return front().second; // should be unreachable! +} + +std::string Dictionary::toString() const +{ + std::string s; + for(const_iterator kv(begin());kv!=end();++kv) { + _appendEsc(kv->first.data(),(unsigned int)kv->first.length(),s); + s.push_back('='); + _appendEsc(kv->second.data(),(unsigned int)kv->second.length(),s); + s.append(ZT_EOL_S); + } + return s; +} + void Dictionary::updateFromString(const char *s,unsigned int maxlen) { bool escapeState = false; @@ -80,6 +142,16 @@ void Dictionary::fromString(const char *s,unsigned int maxlen) updateFromString(s,maxlen); } +void Dictionary::eraseKey(const std::string &key) +{ + for(iterator i(begin());i!=end();++i) { + if (i->first == key) { + this->erase(i); + return; + } + } +} + bool Dictionary::sign(const Identity &id,uint64_t now) { try { diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 1e643788e..24f2ac8ff 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -31,8 +31,9 @@ #include #include -#include +#include #include +#include #include "Constants.hpp" #include "Utils.hpp" @@ -56,12 +57,12 @@ class Identity; * * Keys beginning with "~!" are reserved for signature data fields. * - * Note: the signature code depends on std::map<> being sorted, but no - * other code does. So if the underlying data structure is ever swapped - * out for an unsorted one, the signature code will have to be updated - * to sort before composing the string to sign. + * It's stored as a simple vector and can be linearly scanned or + * binary searched. Dictionaries are only used for very small things + * outside the core loop, so this is not a significant performance + * issue and it reduces memory use and code footprint. */ -class Dictionary : public std::map +class Dictionary : public std::vector< std::pair > { public: Dictionary() {} @@ -77,21 +78,8 @@ public: */ Dictionary(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); } - /** - * Get a key, throwing an exception if it is not present - * - * @param key Key to look up - * @return Reference to value - * @throws std::invalid_argument Key not found - */ - inline const std::string &get(const std::string &key) const - throw(std::invalid_argument) - { - const_iterator e(find(key)); - if (e == end()) - throw std::invalid_argument(std::string("missing required field: ")+key); - return e->second; - } + iterator find(const std::string &key); + const_iterator find(const std::string &key) const; /** * Get a key, returning a default if not present @@ -113,23 +101,7 @@ public: * @param dfl Default boolean result if key not found or empty (default: false) * @return Boolean value of key */ - inline bool getBoolean(const std::string &key,bool dfl = false) const - { - const_iterator e(find(key)); - if (e == end()) - return dfl; - if (e->second.length() < 1) - return dfl; - switch(e->second[0]) { - case '1': - case 't': - case 'T': - case 'y': - case 'Y': - return true; - } - return false; - } + bool getBoolean(const std::string &key,bool dfl = false) const; /** * @param key Key to get @@ -170,6 +142,8 @@ public: return Utils::strTo64(e->second.c_str()); } + std::string &operator[](const std::string &key); + /** * @param key Key to set * @param value String value @@ -239,17 +213,7 @@ public: /** * @return String-serialized dictionary */ - inline std::string toString() const - { - std::string s; - for(const_iterator kv(begin());kv!=end();++kv) { - _appendEsc(kv->first.data(),(unsigned int)kv->first.length(),s); - s.push_back('='); - _appendEsc(kv->second.data(),(unsigned int)kv->second.length(),s); - s.append(ZT_EOL_S); - } - return s; - } + std::string toString() const; /** * Clear and initialize from a string @@ -278,14 +242,19 @@ public: */ uint64_t signatureTimestamp() const; + /** + * @param key Key to erase + */ + void eraseKey(const std::string &key); + /** * Remove any signature from this dictionary */ inline void removeSignature() { - erase(ZT_DICTIONARY_SIGNATURE); - erase(ZT_DICTIONARY_SIGNATURE_IDENTITY); - erase(ZT_DICTIONARY_SIGNATURE_TIMESTAMP); + eraseKey(ZT_DICTIONARY_SIGNATURE); + eraseKey(ZT_DICTIONARY_SIGNATURE_IDENTITY); + eraseKey(ZT_DICTIONARY_SIGNATURE_TIMESTAMP); } /** @@ -305,21 +274,6 @@ public: */ bool verify(const Identity &id) const; - inline bool operator==(const Dictionary &d) const - { - // std::map::operator== is broken on uclibc++ - if (size() != d.size()) - return false; - const_iterator a(begin()); - const_iterator b(d.begin()); - while (a != end()) { - if (*(a++) != *(b++)) - return false; - } - return true; - } - inline bool operator!=(const Dictionary &d) const { return (!(*this == d)); } - private: void _mkSigBuf(std::string &buf) const; static void _appendEsc(const char *data,unsigned int len,std::string &to); diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index e46da4a4b..cd32600f1 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -87,27 +87,27 @@ void NetworkConfig::_fromDictionary(const Dictionary &d) // NOTE: d.get(name) throws if not found, d.get(name,default) returns default - _nwid = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID).c_str()); + _nwid = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,"0").c_str()); if (!_nwid) throw std::invalid_argument("configuration contains zero network ID"); - _timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP).c_str()); + _timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,"0").c_str()); _revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older controllers don't send this, so default to 1 memset(_etWhitelist,0,sizeof(_etWhitelist)); - std::vector ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES).c_str(),",","","")); + std::vector ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","","")); for(std::vector::const_iterator et(ets.begin());et!=ets.end();++et) { unsigned int tmp = Utils::hexStrToUInt(et->c_str()) & 0xffff; _etWhitelist[tmp >> 3] |= (1 << (tmp & 7)); } - _issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO)); + _issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,"0")); _multicastLimit = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,zero).c_str()); if (_multicastLimit == 0) _multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT; _allowPassiveBridging = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING,zero).c_str()) != 0); _private = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE,one).c_str()) != 0); _enableBroadcast = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST,one).c_str()) != 0); - _name = d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME); + _name = d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,""); if (_name.length() > ZT_MAX_NETWORK_SHORT_NAME_LENGTH) throw std::invalid_argument("network short name too long (max: 255 characters)"); diff --git a/node/Topology.cpp b/node/Topology.cpp index 65abfc6f0..908acbc82 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -140,7 +140,7 @@ void Topology::setRootServers(const Dictionary &sn) if ((d->first.length() == ZT_ADDRESS_LENGTH_HEX)&&(d->second.length() > 0)) { try { Dictionary snspec(d->second); - std::vector &a = m[Identity(snspec.get("id"))]; + std::vector &a = m[Identity(snspec.get("id",""))]; std::string udp(snspec.get("udp",std::string())); if (udp.length() > 0) a.push_back(InetAddress(udp));