From 36573e798c986e298c62daabed6036b12ea0314d Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Tue, 2 Feb 2010 20:37:50 +0000
Subject: Use Glib string interning (quarks) to make Path/URI operator== very
 fast.

This avoids a ton of string comparison overhead in Ingen when setting various
properties (e.g. "ingen:value" was compared several times every time a port
value was changed, now this is just a single pointer comparison and the full
round trip of a value change does no string comparison at all, but is still
property based and RDFey).


git-svn-id: http://svn.drobilla.net/lad/trunk/raul@2408 a436a847-0d15-0410-975c-d299462d15a1
---
 raul/Atom.hpp         | 56 ++++++++++++++++++++++++++---------------
 raul/Path.hpp         | 69 ++++++++++++++++++++++++++++-----------------------
 raul/Symbol.hpp       | 28 +++++++++++++++++----
 raul/TableImpl.hpp    |  4 +--
 raul/URI.hpp          | 37 ++++++++++++++-------------
 src/Configuration.cpp |  2 +-
 src/Path.cpp          |  4 +--
 wscript               |  5 ++--
 8 files changed, 125 insertions(+), 80 deletions(-)

diff --git a/raul/Atom.hpp b/raul/Atom.hpp
index 5091453..3451604 100644
--- a/raul/Atom.hpp
+++ b/raul/Atom.hpp
@@ -24,9 +24,11 @@
 #include <cstring>
 #include <string>
 #include <ostream>
+#include <glib.h>
 
 namespace Raul {
 
+class URI;
 
 /** A piece of data with some type.
  *
@@ -54,17 +56,17 @@ public:
 	Atom(bool val)        : _type(BOOL),   _bool_val(val)           {}
 	Atom(const char* val) : _type(STRING), _string_val(strdup(val)) {}
 
-	Atom(Type t, const std::string& val) : _type(t), _string_val(strdup(val.c_str())) {}
+	Atom(const std::string& val) : _type(STRING), _string_val(strdup(val.c_str())) {}
+
+	/** URI constructor (@a t must be URI) */
+	Atom(Type t, const std::string& val) : _type(t), _string_val(g_intern_string(val.c_str())) {
+		assert(t == URI);
+	}
 
 	Atom(const char* type_uri, size_t size, void* val)
 		: _type(BLOB), _blob_val(new BlobValue(type_uri, size, val)) {}
 
-	~Atom() {
-		if (_type == URI || _type == STRING)
-			free(_string_val);
-		else if (_type == BLOB)
-			delete _blob_val;
-	}
+	~Atom() { dealloc(); }
 
 	Atom(const Atom& copy)
 		: _type(copy._type)
@@ -74,18 +76,14 @@ public:
 		case INT:    _int_val    = copy._int_val;                  break;
 		case FLOAT:  _float_val  = copy._float_val;                break;
 		case BOOL:   _bool_val   = copy._bool_val;                 break;
-		case URI:
+		case URI:    _string_val = copy._string_val;               break;
 		case STRING: _string_val = strdup(copy._string_val);       break;
 		case BLOB:   _blob_val   = new BlobValue(*copy._blob_val); break;
 		}
 	}
 
 	Atom& operator=(const Atom& other) {
-		if (_type == BLOB)
-			delete _blob_val;
-		else if (_type == STRING)
-			free(_string_val);
-
+		dealloc();
 		_type = other._type;
 
 		switch (_type) {
@@ -93,7 +91,7 @@ public:
 		case INT:    _int_val    = other._int_val;                  break;
 		case FLOAT:  _float_val  = other._float_val;                break;
 		case BOOL:   _bool_val   = other._bool_val;                 break;
-		case URI:
+		case URI:    _string_val = other._string_val;               break;
 		case STRING: _string_val = strdup(other._string_val);       break;
 		case BLOB:   _blob_val   = new BlobValue(*other._blob_val); break;
 		}
@@ -107,7 +105,7 @@ public:
 			case INT:    return _int_val    == other._int_val;
 			case FLOAT:  return _float_val  == other._float_val;
 			case BOOL:   return _bool_val   == other._bool_val;
-			case URI:
+			case URI:    return _string_val == other._string_val;
 			case STRING: return strcmp(_string_val, other._string_val) == 0;
 			case BLOB:   return _blob_val == other._blob_val;
 			}
@@ -164,6 +162,24 @@ public:
 private:
 	Type _type;
 
+	friend class Raul::URI;
+	Atom(const char* str, uint32_t magic) : _type(URI), _string_val(str) {
+		assert(magic == 12345);
+		assert(g_intern_string(str) == str);
+	}
+
+	inline void dealloc() {
+		switch (_type) {
+		case STRING:
+			free(const_cast<char*>(_string_val));
+			break;
+		case BLOB:
+			delete _blob_val;
+		default:
+			break;
+		}
+	}
+
 	class BlobValue {
 	public:
 		BlobValue(const char* type, size_t size, void* data)
@@ -196,11 +212,11 @@ private:
 	};
 
 	union {
-		int32_t    _int_val;
-		float      _float_val;
-		bool       _bool_val;
-		char*      _string_val;
-		BlobValue* _blob_val;
+		int32_t     _int_val;
+		float       _float_val;
+		bool        _bool_val;
+		const char* _string_val;
+		BlobValue*  _blob_val;
 	};
 };
 
diff --git a/raul/Path.hpp b/raul/Path.hpp
index ffa8aa0..55f3926 100644
--- a/raul/Path.hpp
+++ b/raul/Path.hpp
@@ -58,10 +58,10 @@ public:
 	static const std::string scheme;
 	static const std::string prefix;
 	static const size_t      prefix_len;
-	static const std::string root_uri;
+	static const Path        root;
 
 	/** Construct an uninitialzed path, because the STL is annoying. */
-	Path() : URI(root_uri) {}
+	Path() : URI(root) {}
 
 	/** Construct a Path from an std::string.
 	 *
@@ -99,60 +99,66 @@ public:
 
 	static void replace_invalid_chars(std::string& str, size_t start, bool replace_slash = false);
 
-	bool is_root() const { return str() == root_uri; }
+	bool is_root() const { return (*this) == root; }
 
 	bool is_child_of(const Path& parent) const;
 	bool is_parent_of(const Path& child) const;
 
 	Path child(const std::string& s) const {
 		if (is_valid(s))
-			return std::string(base()) + Path(s).chop_scheme().substr(1);
+			return base() + Path(s).chop_scheme().substr(1);
 		else
-			return std::string(base()) + s;
+			return base() + s;
 	}
 
-	Path operator+(const Path& p) const { return child(p); }
-
-	/** Return the name of this object (everything after the last '/').
-	 * This is the "method name" for OSC paths.
-	 * The empty string may be returned (if the path is "/").
-	 */
-	inline std::string name() const {
-		if (str() == root_uri)
-			return "";
-		else
-			return substr(find_last_of("/")+1);
+	Path child(const Path& p) const {
+		return base() + p.chop_scheme().substr(1);
 	}
 
+	Path operator+(const Path& p) const { return child(p); }
 
-	/** Return the name of this object (everything after the last '/').
-	 * This is the "method name" for OSC paths.
-	 * Note it is illegal to call this method on the path "/".
+	/** Return the symbol of this path (everything after the last '/').
+	 * This is e.g. the "method name" for OSC paths, the filename
+	 * for filesystem paths, etc.
+	 * The empty string may be returned (if the path is the root path).
 	 */
-	inline Symbol symbol() const {
-		return substr(find_last_of("/")+1);
+	inline const char* symbol() const {
+		if ((*this) != root) {
+			const char* last_slash = strrchr(c_str(), '/');
+			if (last_slash) {
+				return last_slash + 1;
+			}
+		}
+		return "";
 	}
 
-
 	/** Return the parent's path.
 	 *
 	 * Calling this on the path "/" will return "/".
 	 * This is the (deepest) "container path" for OSC paths.
 	 */
 	inline Path parent() const {
-		if (str() == root_uri) {
-			return str();
+		if ((*this) == root) {
+			return *this;
 		} else {
-			size_t last_slash = find_last_of("/");
-			return (last_slash == prefix_len) ? root_uri : substr(0, last_slash);
+			const std::string str(this->str());
+			const size_t last_slash = str.find_last_of('/');
+			return (last_slash == prefix_len) ? root : str.substr(0, last_slash);
 		}
 	}
 
 
+	/** Return the path's child with the given name (symbol)
+	 */
+	inline Path child(const Raul::Symbol& symbol) const {
+		return base() + symbol.c_str();
+	}
+
+
 	/** Return path relative to some base path (chop prefix)
 	 */
 	inline Path relative_to_base(const Path& base) const {
-		if (str() == base) {
+		if ((*this) == base) {
 			return "/";
 		} else {
 			assert(length() > base.length());
@@ -167,10 +173,11 @@ public:
 	 * child path can be made using parent.base() + child_name.
 	 */
 	inline const std::string base() const {
-		if (str() == root_uri)
-			return str();
+		std::string ret = str();
+		if ((*this) == root && ret[ret.length() - 1] == '/')
+			return ret;
 		else
-			return str() + "/";
+			return ret + '/';
 	}
 
 	/** Return path with a trailing "/".
@@ -187,7 +194,7 @@ public:
 	static bool descendant_comparator(const Path& parent, const Path& child) {
 		return ( child == parent || (child.length() > parent.length() &&
 				(!std::strncmp(parent.c_str(), child.c_str(), parent.length())
-						&& (parent.str() == root_uri || child[parent.length()] == '/'))) );
+						&& (parent == root || child.str()[parent.length()] == '/'))) );
 	}
 };
 
diff --git a/raul/Symbol.hpp b/raul/Symbol.hpp
index 8aa82c1..1a57886 100644
--- a/raul/Symbol.hpp
+++ b/raul/Symbol.hpp
@@ -23,6 +23,7 @@
 #include <string>
 #include <cstring>
 #include <cassert>
+#include <glib.h>
 
 namespace Raul {
 
@@ -38,16 +39,15 @@ namespace Raul {
  *
  * \ingroup raul
  */
-class Symbol : public std::basic_string<char> {
+class Symbol {
 public:
-
 	/** Construct a Symbol from an std::string.
 	 *
 	 * It is a fatal error to construct a Symbol from an invalid string,
 	 * use is_valid first to check.
 	 */
 	Symbol(const std::basic_string<char>& symbol)
-		: std::basic_string<char>(symbol)
+		: _str(g_intern_string(symbol.c_str()))
 	{
 		assert(is_valid(symbol));
 	}
@@ -59,17 +59,35 @@ public:
 	 * use is_valid first to check.
 	 */
 	Symbol(const char* csymbol)
-		: std::basic_string<char>(csymbol)
+		: _str(g_intern_string(csymbol))
 	{
 		assert(is_valid(csymbol));
 	}
 
-	static bool is_valid(const std::basic_string<char>& path);
+	inline const char* c_str() const { return _str; }
+
+	inline bool operator==(const Symbol& other) const {
+		return _str == other._str;
+	}
+
+	inline bool operator!=(const Symbol& other) const {
+		return _str != other._str;
+	}
+
+	static bool is_valid(const std::basic_string<char>& symbol);
 
 	static std::string symbolify(const std::basic_string<char>& str);
+
+private:
+	const char* _str;
 };
 
 
 } // namespace Raul
 
+static inline std::ostream& operator<<(std::ostream& os, const Raul::Symbol& symbol)
+{
+	return (os << symbol.c_str());
+}
+
 #endif // RAUL_SYMBOL_HPP
diff --git a/raul/TableImpl.hpp b/raul/TableImpl.hpp
index 0b32a29..6b91d01 100644
--- a/raul/TableImpl.hpp
+++ b/raul/TableImpl.hpp
@@ -264,7 +264,7 @@ Table<K,T>::insert(const std::pair<K, T>& entry)
 	const K& key = entry.first;
 	const T& value = entry.second;
 
-	if (size() == 0 || (size() == 1 && key > _entries[0].first)) {
+	if (size() == 0 || (size() == 1 && _entries[0].first < key)) {
 		_entries.push_back(entry);
 		return std::make_pair(iterator(*this, size()-1), true);
 	} else if (size() == 1) {
@@ -291,7 +291,7 @@ Table<K,T>::insert(const std::pair<K, T>& entry)
 		if (elem.first == key) {
 			elem.second = value;
 			return std::make_pair(iterator(*this, i), false);
-		} else if (elem.first > key) {
+		} else if (key < elem.first) {
 			if (i == 0 || _entries[i-1].first < key)
 				break;
 			upper = i - 1;
diff --git a/raul/URI.hpp b/raul/URI.hpp
index 18e15ad..18fc8a2 100644
--- a/raul/URI.hpp
+++ b/raul/URI.hpp
@@ -19,8 +19,11 @@
 #define RAUL_URI_HPP
 
 #include <string>
+#include <cstring>
 #include <exception>
 #include <ostream>
+#include <glib.h>
+#include "raul/Atom.hpp"
 
 namespace Raul {
 
@@ -30,7 +33,7 @@ namespace Raul {
  * This "should" be used for proper URIs (RFC3986), but not much support or
  * validation is built-in yet.  The URI string MUST have a scheme though.
  */
-class URI : protected std::basic_string<char> {
+class URI {
 public:
 	class BadURI : public std::exception {
 	public:
@@ -46,7 +49,7 @@ public:
 	 * It is a fatal error to construct a URI from an invalid string,
 	 * use is_valid first to check.
 	 */
-	URI(const std::basic_string<char>& uri="nil:0") : std::basic_string<char>(uri) {
+	URI(const std::basic_string<char>& uri="nil:0") : _str(g_intern_string(uri.c_str())) {
 		if (!is_valid(uri))
 			throw BadURI(uri);
 	}
@@ -56,7 +59,7 @@ public:
 	 * It is a fatal error to construct a URI from an invalid string,
 	 * use is_valid first to check.
 	 */
-	URI(const char* uri) : std::basic_string<char>(uri) {
+	URI(const char* uri) : _str(g_intern_string(uri)) {
 		if (!is_valid(uri))
 			throw BadURI(uri);
 	}
@@ -65,7 +68,7 @@ public:
 		return uri.find(":") != std::string::npos;
 	}
 
-	/** Return path with every up to and including the first occurence of str */
+	/** Return path with everything up to and including the first occurence of str chopped */
 	inline const std::string chop_start(const std::string& str) const {
 		return substr(find(str) + str.length());
 	}
@@ -76,33 +79,33 @@ public:
 	/** Return the URI scheme (everything before the first ':') */
 	inline std::string scheme() const { return substr(0, find(":")); }
 
-	inline const std::string& str()   const { return *this; }
-	inline const char*        c_str() const { return str().c_str(); }
-
-	inline operator const std::string() const { return str(); }
-	inline operator       std::string()       { return str(); }
+	inline const std::string str()   const { return _str; }
+	inline const char*       c_str() const { return _str; }
 
 	inline std::string substr(size_t start, size_t end=std::string::npos) const {
 		return str().substr(start, end);
 	}
 
-	inline bool operator<(const URI& uri)  const { return str() <  uri; }
-	inline bool operator<=(const URI& uri) const { return str() <= uri; }
-	inline bool operator>(const URI& uri)  const { return str() >  uri; }
-	inline bool operator>=(const URI& uri) const { return str() >= uri; }
-	inline bool operator==(const URI& uri) const { return str() == uri; }
-	inline bool operator!=(const URI& uri) const { return str() != uri; }
+	inline bool operator<(const URI& uri)  const { return strcmp(_str, uri.c_str()) < 0; }
+	inline bool operator<=(const URI& uri) const { return (*this) == uri || (*this) < uri; }
+	inline bool operator==(const URI& uri) const { return _str == uri._str; }
+	inline bool operator!=(const URI& uri) const { return _str != uri._str; }
 
 	inline size_t length()                           const { return str().length(); }
 	inline size_t find(const std::string& s)         const { return str().find(s); }
-	inline size_t find_last_of(const std::string& s) const { return str().find_last_of(s); }
+	inline size_t find_last_of(char c) const { return str().find_last_of(c); }
+
+	inline operator Raul::Atom() const { return Raul::Atom(_str, 12345); }
+
+private:
+	const char* _str;
 };
 
 static inline
 std::ostream&
 operator<<(std::ostream& os, const URI& uri)
 {
-	return (os << uri.str());
+	return (os << uri.c_str());
 }
 
 } // namespace Raul
diff --git a/src/Configuration.cpp b/src/Configuration.cpp
index 1ea70a3..bc6cfbd 100644
--- a/src/Configuration.cpp
+++ b/src/Configuration.cpp
@@ -85,7 +85,7 @@ Configuration::set_value_from_string(Configuration::Option& option, const std::s
 		}
 		break;
 	case Atom::STRING:
-		option.value = Atom(Atom::STRING, value);
+		option.value = Atom(value.c_str());
 		break;
 	default:
 		throw CommandLineError(string("bad option type `--") + option.name + "'");
diff --git a/src/Path.cpp b/src/Path.cpp
index 512d91b..c0eb0df 100644
--- a/src/Path.cpp
+++ b/src/Path.cpp
@@ -24,7 +24,7 @@ namespace Raul {
 const string Path::scheme     = "path";
 const string Path::prefix     = Path::scheme + ":";
 const size_t Path::prefix_len = prefix.length();
-const string Path::root_uri   = Path::prefix + "/";
+const Path   Path::root   = Path::prefix + "/";
 
 bool
 Path::is_valid(const std::basic_string<char>& path_str)
@@ -73,7 +73,7 @@ string
 Path::pathify(const std::basic_string<char>& str)
 {
 	if (str.length() == 0)
-		return root_uri; // this might not be wise?
+		return root.str(); // this might not be wise?
 
 	string path  = (str.substr(0, prefix_len) == prefix) ? str : prefix + str;
 	size_t start = prefix_len + 1;
diff --git a/wscript b/wscript
index 8461534..d34f47e 100644
--- a/wscript
+++ b/wscript
@@ -4,7 +4,7 @@ import Options
 import os
 
 # Version of this package (even if built as a child)
-RAUL_VERSION = '0.6.2'
+RAUL_VERSION = '0.6.3'
 
 # Library version (UNIX style major, minor, micro)
 # major increment <=> incompatible changes
@@ -17,7 +17,8 @@ RAUL_VERSION = '0.6.2'
 #   0.6.0 = 3,0,0
 #   0.6.1 = 4,0,0 (unreleased)
 #   0.6.2 = 5,0,0 (unreleased)
-RAUL_LIB_VERSION = '5.0.0'
+#   0.6.3 = 6,0,0 (unreleased)
+RAUL_LIB_VERSION = '6.0.0'
 
 # Variables for 'waf dist'
 APPNAME = 'raul'
-- 
cgit v1.2.1