Browse Source

Simplify Dictionary and reduce memory usage, now no more std::maps in core.

Adam Ierymenko 9 years ago
parent
commit
5384f185ae
4 changed files with 99 additions and 73 deletions
  1. 72 0
      node/Dictionary.cpp
  2. 21 67
      node/Dictionary.hpp
  3. 5 5
      node/NetworkConfig.cpp
  4. 1 1
      node/Topology.cpp

+ 72 - 0
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<std::string,std::string>(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 {

+ 21 - 67
node/Dictionary.hpp

@@ -31,8 +31,9 @@
 #include <stdint.h>
 
 #include <string>
-#include <map>
+#include <vector>
 #include <stdexcept>
+#include <algorithm>
 
 #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<std::string,std::string>
+class Dictionary : public std::vector< std::pair<std::string,std::string> >
 {
 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);

+ 5 - 5
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<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES).c_str(),",","",""));
+	std::vector<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","",""));
 	for(std::vector<std::string>::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)");
 

+ 1 - 1
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<InetAddress> &a = m[Identity(snspec.get("id"))];
+				std::vector<InetAddress> &a = m[Identity(snspec.get("id",""))];
 				std::string udp(snspec.get("udp",std::string()));
 				if (udp.length() > 0)
 					a.push_back(InetAddress(udp));