diff options
author | David Robillard <d@drobilla.net> | 2012-08-14 04:22:07 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2012-08-14 04:22:07 +0000 |
commit | 2a429ca76b97cca197f105b665271360b74f6917 (patch) | |
tree | d25d206910bd4a84050918693816240c26d7d0ae | |
parent | 8bf87dc2367caf9d82dbda0382363cda400971dc (diff) | |
download | raul-2a429ca76b97cca197f105b665271360b74f6917.tar.gz raul-2a429ca76b97cca197f105b665271360b74f6917.tar.bz2 raul-2a429ca76b97cca197f105b665271360b74f6917.zip |
Remove glib dependency.
Make Symbol, URI, and Path simpler derivatives of std::string.
100% test coverage by line for Symbol, URI, Path, AtomicInt, and AtomicPtr.
Add Raul::Exception.
git-svn-id: http://svn.drobilla.net/lad/trunk/raul@4686 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | raul.pc.in | 1 | ||||
-rw-r--r-- | raul/Configuration.hpp | 10 | ||||
-rw-r--r-- | raul/Exception.hpp | 34 | ||||
-rw-r--r-- | raul/Path.hpp | 219 | ||||
-rw-r--r-- | raul/Symbol.hpp | 112 | ||||
-rw-r--r-- | raul/URI.hpp | 115 | ||||
-rw-r--r-- | src/Path.cpp | 164 | ||||
-rw-r--r-- | src/Symbol.cpp | 67 | ||||
-rw-r--r-- | test/path_test.cpp | 64 | ||||
-rw-r--r-- | test/symbol_test.cpp | 77 | ||||
-rw-r--r-- | test/table_test.cpp | 5 | ||||
-rw-r--r-- | test/uri_test.cpp | 61 | ||||
-rw-r--r-- | wscript | 10 |
14 files changed, 446 insertions, 498 deletions
@@ -1,12 +1,13 @@ raul (9999) unstable; + * Remove glib dpendency * Improve RingBuffer + * Add ThreadVar, a thread-specific variable class * Improve test suite * Remove EventRingBuffer * Remove SMFReader and SMFWriter * Remove OSC and RDF library dependent code - * Remove Thread context stuff and add a thread-specific variable class, - ThreadVar, which can be used for this and many other things + * Remove Thread context stuff in favour of ThreadVar * Add INSTALL file * Update license to GPL3+ @@ -6,6 +6,5 @@ includedir=@INCLUDEDIR@ Name: raul Version: @RAUL_VERSION@ Description: A C++ convenience library for realtime audio applications -Requires: glib-2.0 gthread-2.0 Libs: -L${libdir} -lraul @RAUL_PC_LIBS@ Cflags: -I${includedir} diff --git a/raul/Configuration.hpp b/raul/Configuration.hpp index 554b247..6e45d3c 100644 --- a/raul/Configuration.hpp +++ b/raul/Configuration.hpp @@ -22,12 +22,13 @@ #include <stdlib.h> #include <string.h> -#include <exception> #include <list> #include <map> #include <ostream> #include <string> +#include "raul/Exception.hpp" + namespace Raul { /** Program configuration (command line options and/or configuration file). @@ -126,11 +127,8 @@ public: void print_usage(const std::string& program, std::ostream& os); - struct CommandLineError : public std::exception { - explicit CommandLineError(const std::string& m) : msg(m) {} - ~CommandLineError() throw() {} - const char* what() const throw() { return msg.c_str(); } - std::string msg; + struct CommandLineError : public Exception { + explicit CommandLineError(const std::string& m) : Exception(m) {} }; void parse(int argc, char** argv) throw (CommandLineError); diff --git a/raul/Exception.hpp b/raul/Exception.hpp new file mode 100644 index 0000000..4969ab2 --- /dev/null +++ b/raul/Exception.hpp @@ -0,0 +1,34 @@ +/* + This file is part of Raul. + Copyright 2007-2012 David Robillard <http://drobilla.net> + + Raul is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or any later version. + + Raul is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Raul. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <exception> +#include <string> + +#ifndef RAUL_EXCEPTION_HPP +#define RAUL_EXCEPTION_HPP + +/** An exception (unexpected error). */ +class Exception : public std::exception { +public: + const char* what() const throw() { return _what.c_str(); } + ~Exception() throw() {} +protected: + explicit Exception(const std::string& what) : _what(what) {} +private: + const std::string _what; +}; + +#endif // RAUL_EXCEPTION_HPP diff --git a/raul/Path.hpp b/raul/Path.hpp index 57bed80..05fb029 100644 --- a/raul/Path.hpp +++ b/raul/Path.hpp @@ -17,48 +17,38 @@ #ifndef RAUL_PATH_HPP #define RAUL_PATH_HPP -#include <cassert> -#include <cctype> -#include <cstring> -#include <exception> #include <string> +#include "raul/Exception.hpp" #include "raul/Symbol.hpp" namespace Raul { /** A restricted path of Symbols separated by, and beginning with, "/". * - * This enforces that a Path is a valid path, where each fragment is a valid - * Symbol, separated by exactly one slash (/). + * A Path never ends with a "/", except for the root Path "/", which is the + * only valid single-character Path. * - * A path is divided by slashes (/). The first character MUST be a slash, and - * the last character MUST NOT be a slash (except in the special case of the - * root path "/", which is the only valid single-character path). - * - * \ingroup raul + * @ingroup raul */ -class Path { +class Path : public std::basic_string<char> { public: - class BadPath : public std::exception { + /** Attempt to construct an invalid Path. */ + class BadPath : public Exception { public: - explicit BadPath(const std::string& path) : _path(path) {} - ~BadPath() throw() {} - const char* what() const throw() { return _path.c_str(); } - private: - const std::string _path; + explicit BadPath(const std::string& path) : Exception(path) {} }; /** Construct an uninitialzed path, because the STL is annoying. */ - Path() : _str(g_intern_string("/")) {} + Path() : std::basic_string<char>("/") {} - /** Construct a Path from an std::string. + /** Construct a Path from a C++ string. * - * It is a fatal error to construct a Path from an invalid string, - * use is_valid first to check. + * This will throw an exception if @p path is invalid. To avoid this, use + * is_valid() first to check. */ - Path(const std::basic_string<char>& path) throw (BadPath) - : _str(g_intern_string(path.c_str())) + explicit Path(const std::basic_string<char>& path) + : std::basic_string<char>(path) { if (!is_valid(path)) { throw BadPath(path); @@ -67,56 +57,81 @@ public: /** Construct a Path from a C string. * - * It is a fatal error to construct a Path from an invalid string, - * use is_valid first to check. + * This will throw an exception if @p path is invalid. To avoid this, use + * is_valid() first to check. */ - Path(const char* cpath) throw(BadPath) - : _str(g_intern_string(cpath)) + explicit Path(const char* path) + : std::basic_string<char>(path) { - if (!is_valid(cpath)) { - throw BadPath(cpath); + if (!is_valid(path)) { + throw BadPath(path); } } - /** Construct a Path from another path. + /** Copy a Path. * - * This is faster than constructing a path from the other path's string - * representation, since validity checking is avoided. + * Note this is faster than constructing a Path from another Path's string + * since validation is unnecessary. */ - Path& operator=(const Path& other) { - _str = other._str; - return *this; + Path(const Path& path) + : std::basic_string<char>(path) + {} + + /** Return true iff @p c is a valid Path character. */ + static inline bool is_valid_char(char c) { + return c == '/' || Symbol::is_valid_char(c); } - static bool is_valid(const std::basic_string<char>& path); + /** Return true iff @p str is a valid Path. */ + static inline bool is_valid(const std::basic_string<char>& str) { + if (str.empty() || (str[0] != '/')) { + return false; // Must start with '/' + } - /** Convert a string to a valid Path. */ - static Path pathify(const std::basic_string<char>& str); + if (str != "/" && *str.rbegin() == '/') { + return false; // Must not end with '/' except for the root + } - static void replace_invalid_chars(std::string& str, - size_t start, - bool replace_slash = false); + for (size_t i = 1; i < str.length(); ++i) { + if (!is_valid_char(str[i])) { + return false; // All characters must be /, _, a-z, A-Z, 0-9 + } else if (str[i - 1] == '/') { + if (str[i] == '/') { + return false; // Must not contain "//" + } else if (!Symbol::is_valid_start_char(str[i])) { + return false; // Invalid symbol start character (digit) + } + } + } - bool is_root() const { return !strcmp(_str, "/"); } + return true; + } - bool is_child_of(const Path& parent) const; - bool is_parent_of(const Path& child) const; + /** Return true iff this path is the root path ("/"). */ + inline bool is_root() const { return *this == "/"; } - Path operator+(const Path& p) const { return child(p); } + /** Return true iff this path is a child of @p parent at any depth. */ + inline bool is_child_of(const Path& parent) const { + const std::string parent_base = parent.base(); + return substr(0, parent_base.length()) == parent_base; + } - /** Return the lowest common ancestor of a and b. */ - static Path lca(const Path& a, const Path& b); + /** Return true iff this path is a parent of @p child at any depth. */ + inline bool is_parent_of(const Path& child) const { + return child.is_child_of(*this); + } /** 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). + * + * This is what is called the "basename" for file paths, the "method name" + * for OSC paths, and so on. Since the root path does not have a symbol, + * this does not return a Raul::Symbol but may return the empty string. */ inline const char* symbol() const { if (!is_root()) { - const char* last_slash = strrchr(c_str(), '/'); - if (last_slash) { - return last_slash + 1; + const size_t last_sep = rfind('/'); + if (last_sep != std::string::npos) { + return c_str() + last_sep + 1; } } return ""; @@ -131,83 +146,65 @@ public: if (is_root()) { return *this; } else { - const std::string str(this->str()); - const size_t first_slash = str.find('/'); - const size_t last_slash = str.find_last_of('/'); - return (first_slash == last_slash) ? Path("/") : str.substr(0, last_slash); + const size_t first_sep = find('/'); + const size_t last_sep = find_last_of('/'); + return (first_sep == last_sep) ? Path("/") : Path(substr(0, last_sep)); } } - Path child(const Path& p) const { - return base() + p.str().substr(1); + /** Return a child Path of this path. */ + inline Path child(const Path& p) const { + return p.is_root() ? *this : Path(base() + p.substr(1)); } - /** Return the path's child with the given symbol - */ + /** Return a direct child Path of this Path with the given Symbol. */ inline Path child(const Raul::Symbol& symbol) const { - return base() + symbol.c_str(); + return Path(base() + symbol.c_str()); } - /** Return path relative to some base path (chop prefix) + /** Return path with a trailing "/". + * + * The returned string is such that appending a valid Symbol to it is + * guaranteed to form a valid path. */ - inline Path relative_to_base(const Path& base) const { - if ((*this) == base) { - return "/"; + inline const std::string base() const { + if (is_root()) { + return *this; } else { - assert(length() > base.base().length()); - return str().substr(base.base().length() - 1); + return *this + '/'; } } - /** Return path with a trailing "/". - * - * Returned value is guaranteed to be a valid parent path, i.e. a valid - * child path can be made using parent.base() + symbol. - */ - inline const std::string base() const { - std::string ret = str(); - if (is_root() && ret[ret.length() - 1] == '/') - return ret; - else - return ret + '/'; + /** Return the lowest common ancestor of a and b. */ + static inline Path lca(const Path& a, const Path& b) { + const size_t len = std::min(a.length(), b.length()); + size_t last_sep = 0; + for (size_t i = 0; i < len; ++i) { + if (a[i] == '/' && b[i] == '/') { + last_sep = i; + } + if (a[i] != b[i]) { + break; + } + } + + if (last_sep <= 1) { + return Path("/"); + } + + return Path(a.substr(0, last_sep)); } - /** Return true if \a child is equal to, or a descendant of \a parent */ - static bool descendant_comparator(const Path& parent, const Path& child) { + /** Return true iff @p child is equal to, or a descendant of @p parent. */ + static inline 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.is_root() || child.str()[parent.length()] == '/'))) ); - } - - inline std::string substr(size_t start, size_t end=std::string::npos) const { - return str().substr(start, end); + (!parent.compare(0, parent.length(), child, 0, parent.length()) + && (parent.is_root() || child[parent.length()] == '/')))); } - - inline bool operator<(const Path& path) const { return strcmp(_str, path.c_str()) < 0; } - inline bool operator<=(const Path& path) const { return (*this) == path || (*this) < path; } - inline bool operator==(const Path& path) const { return _str == path._str; } - inline bool operator!=(const Path& path) const { return _str != path._str; } - - inline char operator[](int i) const { return _str[i]; } - - inline size_t length() const { return str().length(); } - inline size_t find(const std::string& s) const { return str().find(s); } - - inline const std::string str() const { return _str; } - inline const char* c_str() const { return _str; } - -private: - const char* _str; }; } // namespace Raul -static inline -std::ostream& -operator<<(std::ostream& os, const Raul::Path& path) -{ - return (os << path.c_str()); -} - #endif // RAUL_PATH_HPP diff --git a/raul/Symbol.hpp b/raul/Symbol.hpp index dcf1f04..3f14cc2 100644 --- a/raul/Symbol.hpp +++ b/raul/Symbol.hpp @@ -17,45 +17,38 @@ #ifndef RAUL_SYMBOL_HPP #define RAUL_SYMBOL_HPP -#include <cctype> -#include <cstring> -#include <exception> -#include <iostream> #include <string> -#include <glib.h> +#include "raul/Exception.hpp" namespace Raul { -/** A restricted string (C identifier, which is a component of a Path). +/** A restricted string which is a valid C identifier and Path component. * * A Symbol is a very restricted string suitable for use as an identifier. * It is a valid LV2 symbol, URI fragment, filename, OSC path fragment, - * and identifier for most programming languages (including C). + * and identifier for virtually all programming languages. * * Valid characters are _, a-z, A-Z, 0-9, except the first character which * must not be 0-9. * - * \ingroup raul + * @ingroup raul */ -class Symbol { +class Symbol : public std::basic_string<char> { public: - class BadSymbol : public std::exception { + /** Attempt to construct an invalid Symbol. */ + class BadSymbol : public Exception { public: - explicit BadSymbol(const std::string& symbol) : _symbol(symbol) {} - ~BadSymbol() throw() {} - const char* what() const throw() { return _symbol.c_str(); } - private: - const std::string _symbol; + explicit BadSymbol(const std::string& symbol) : Exception(symbol) {} }; - /** Construct a Symbol from an std::string. + /** Construct a Symbol from a C++ string. * - * It is a fatal error to construct a Symbol from an invalid string, - * use is_valid first to check. + * This will throw an exception if @p symbol is invalid. To avoid this, + * use is_valid() first to check. */ - explicit Symbol(const std::basic_string<char>& symbol) throw(BadSymbol) - : _str(g_intern_string(symbol.c_str())) + explicit Symbol(const std::basic_string<char>& symbol) + : std::basic_string<char>(symbol) { if (!is_valid(symbol)) { throw BadSymbol(symbol); @@ -64,50 +57,71 @@ public: /** Construct a Symbol from a C string. * - * It is a fatal error to construct a Symbol from an invalid string, - * use is_valid first to check. + * This will throw an exception if @p symbol is invalid. To avoid this, + * use is_valid() first to check. */ - explicit Symbol(const char* csymbol) throw(BadSymbol) - : _str(g_intern_string(csymbol)) + explicit Symbol(const char* symbol) + : std::basic_string<char>(symbol) { - if (!is_valid(csymbol)) { - throw BadSymbol(csymbol); + if (!is_valid(symbol)) { + throw BadSymbol(symbol); } } - Symbol& operator=(const Symbol& other) { - _str = other._str; - return *this; - } - - inline const char* c_str() const { return _str; } + /** Copy a Symbol. + * + * Note this is faster than constructing a Symbol from another Symbol's + * string since validation is unnecessary. + */ + Symbol(const Symbol& symbol) + : std::basic_string<char>(symbol) + {} - inline bool operator==(const Symbol& other) const { - return _str == other._str; + /** Return true iff @p c is a valid Symbol start character. */ + static inline bool is_valid_start_char(char c) { + return (c == '_') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } - inline bool operator!=(const Symbol& other) const { - return _str != other._str; + /** Return true iff @p c is a valid Symbol character. */ + static inline bool is_valid_char(char c) { + return is_valid_start_char(c) || (c >= '0' && c <= '9'); } - inline bool operator<(const Symbol& other) const { - return strcmp(_str, other._str) < 0; - } + /** Return true iff @p str is a valid Symbol. */ + static inline bool is_valid(const std::basic_string<char>& str) { + if (str.empty() || (str[0] >= '0' && str[0] <= '9')) { + return false; // Must start with a letter or underscore + } - static bool is_valid(const std::basic_string<char>& symbol); + for (size_t i = 0; i < str.length(); ++i) { + if (!is_valid_char(str[i])) { + return false; // All characters must be _, a-z, A-Z, 0-9 + } + } - static Raul::Symbol symbolify(const std::basic_string<char>& str); + return true; + } + + /** Convert a string to a valid symbol. + * + * This will make a best effort at turning @a str into a complete, valid + * Symbol, and will always return one. + */ + static inline Symbol symbolify(const std::basic_string<char>& in) { + if (in.empty()) { + return Symbol("_"); + } -private: - const char* _str; + std::basic_string<char> out(in); + for (size_t i = 0; i < in.length(); ++i) { + if (!is_valid_char(out[i])) { + out[i] = '_'; + } + } + return Symbol(out); + } }; } // 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/URI.hpp b/raul/URI.hpp index 83454ca..b2ccad7 100644 --- a/raul/URI.hpp +++ b/raul/URI.hpp @@ -18,39 +18,30 @@ #define RAUL_URI_HPP #include <string> -#include <cstring> -#include <exception> -#include <ostream> -#include <glib.h> -#include "raul/Path.hpp" +#include "raul/Exception.hpp" namespace Raul { -/** Simple wrapper around standard string with useful URI-specific methods. +/** A URI (RFC3986) string. * - * 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. - * \ingroup raul + * @ingroup raul */ -class URI { +class URI : public std::basic_string<char> { public: - class BadURI : public std::exception { + /** Attempt to construct an invalid URI. */ + class BadURI : public Exception { public: - explicit BadURI(const std::string& uri) : _uri(uri) {} - ~BadURI() throw() {} - const char* what() const throw() { return _uri.c_str(); } - private: - const std::string _uri; + explicit BadURI(const std::string& uri) : Exception(uri) {} }; - /** Construct a URI from an std::string. + /** Construct a URI from a C++ string. * - * It is a fatal error to construct a URI from an invalid string, - * use is_valid first to check. + * This will throw an exception if @p uri is invalid. To avoid this, use + * is_valid() first to check. */ - URI(const std::basic_string<char>& uri="nil:0") throw(BadURI) - : _str(g_intern_string(uri.c_str())) + explicit URI(const std::basic_string<char>& uri) + : std::basic_string<char>(uri) { if (!is_valid(uri)) { throw BadURI(uri); @@ -59,67 +50,63 @@ public: /** Construct a URI from a C string. * - * It is a fatal error to construct a URI from an invalid string, - * use is_valid first to check. + * This will throw an exception if @p uri is invalid. To avoid this, use + * is_valid() first to check. */ - URI(const char* uri) throw(BadURI) - : _str(g_intern_string(uri)) + explicit URI(const char* uri) + : std::basic_string<char>(uri) { - if (!is_valid(uri)) + if (!is_valid(uri)) { throw BadURI(uri); + } } - /** Construct a URI from a base URI and a Path. */ - URI(const URI& base, const Path& path) - : _str(g_intern_string((base.str() + path.c_str()).c_str())) + /** Copy a URI. + * + * Note this is faster than constructing a URI from another URI's string + * since validation is unnecessary. + */ + URI(const URI& uri) + : std::basic_string<char>(uri) {} - static bool is_valid(const std::basic_string<char>& uri) { - return uri.find(":") != std::string::npos; + /** Return true iff @p c is a valid URI start character. */ + static inline bool is_valid_start_char(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } - /** 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()); + /** Return true iff @p c is a valid URI scheme character. */ + static inline bool is_valid_scheme_char(char c) { + // S3.1: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + return is_valid_start_char(c) || (c >= '0' && c <= '9') || + c == '+' || c == '-' || c == '.'; } - /** Return the URI with the scheme removed (as a string) */ - std::string chop_scheme() const { return chop_start(":"); } - - /** Return the URI scheme (everything before the first ':') */ - inline std::string scheme() const { return substr(0, find(":")); } + /** Return true iff @p str is a valid URI. + * + * Currently this only checks that @p starts with a valid URI scheme. + */ + static inline bool is_valid(const std::basic_string<char>& str) { + if (!is_valid_start_char(str[0])) { + return false; // Must start with a-z A-Z + } - inline const std::string str() const { return _str; } - inline const char* c_str() const { return _str; } + for (size_t i = 1; i < str.length(); ++i) { + if (str[i] == ':') { + return true; // Starts with a valid scheme + } else if (!is_valid_scheme_char(str[i])) { + return false; // Invalid scheme character encountered + } + } - inline std::string substr(size_t start, size_t end=std::string::npos) const { - return str().substr(start, end); + return false; // Must start with a scheme followed by ':' } - 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 char operator[](int i) const { return _str[i]; } - - 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(char c) const { return str().find_last_of(c); } - -private: - const char* _str; + /** Return the URI scheme (everything before the first ':') */ + inline std::string scheme() const { return substr(0, find(":")); } }; } // namespace Raul -static inline -std::ostream& -operator<<(std::ostream& os, const Raul::URI& uri) -{ - return (os << uri.c_str()); -} - - #endif // RAUL_URI_HPP diff --git a/src/Path.cpp b/src/Path.cpp deleted file mode 100644 index e3a529d..0000000 --- a/src/Path.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard <http://drobilla.net> - - Raul is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or any later version. - - Raul is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Raul. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "raul/Path.hpp" - -using std::string; - -namespace Raul { - -bool -Path::is_valid(const std::basic_string<char>& path) -{ - if (path.empty() || path[0] != '/') { - return false; - } - - // Root path - if (path == "/") { - return true; - } - - // Not root, must not end with a slash - if (*path.rbegin() == '/') - return false; - - // Double slash not allowed - if (path.find("//") != string::npos) - return false; - - for (size_t i=0; i < path.length(); ++i) - // All contained symbols must not start with a digit - if (i > 0 && path[i-1] == '/' && isdigit(path[i])) - return false; - // All characters must be _, a-z, A-Z, 0-9 - else if ( path[i] != '/' && path[i] != '_' - && (path[i] < 'a' || path[i] > 'z') - && (path[i] < 'A' || path[i] > 'Z') - && (path[i] < '0' || path[i] > '9') ) - return false; - - return true; -} - -Path -Path::pathify(const std::basic_string<char>& str) -{ - if (str.length() == 0) - return Path("/"); // this might not be wise? - - const size_t first_slash = str.find('/'); - string path = (first_slash == string::npos) - ? string("/").append(str) - : str.substr(first_slash); - - // Must start with a / - if (path.empty() || path[0] != '/') - path = string("/").append(path); - - // Must not end with a slash unless "/" - if (path != "/" && path[path.length() - 1] == '/') - path = path.substr(0, path.length() - 1); // chop trailing slash - - replace_invalid_chars(path, 0, false); - return Path(path); -} - -/** Replace any invalid characters in @a str with a suitable replacement. - */ -void -Path::replace_invalid_chars(std::string& str, size_t start, bool replace_slash) -{ - string prefix = str.substr(0, start); - str = str.substr(start); - - size_t open_bracket = str.find_first_of('('); - if (open_bracket != string::npos) - str = str.substr(0, open_bracket); - - open_bracket = str.find_first_of('['); - if (open_bracket != string::npos) - str = str.substr(0, open_bracket); - - if (str[str.length()-1] == ' ') - str = str.substr(0, str.length()-1); - - if (isdigit(str[0])) - str = string("_").append(str); - - for (size_t i=0; i < str.length(); ++i) { - if (i > 0 && str[i-1] == '/' && isdigit(str[i])) { - str = str.substr(0, i) + "_" + str.substr(i); - } else if (str[i] == '\'') { - str = str.substr(0, i) + str.substr(i+1); - } else if ( str[i] != '_' && str[i] != '/' - && (str[i] < 'a' || str[i] > 'z') - && (str[i] < 'A' || str[i] > 'Z') - && (str[i] < '0' || str[i] > '9') ) { - if (i > 0 && str[i-1] == '_') { - str = str.substr(0, i) + str.substr(i+1); - --i; - } else { - str[i] = '_'; - } - } else if (replace_slash && str[i] == '/') { - str[i] = '_'; - } - } - - if (str.length() != 1 && str[str.length()-1] == '_') - str = str.substr(0, str.length()-1); - - str = prefix + str; -} - -bool -Path::is_child_of(const Path& parent) const -{ - const string parent_base = parent.base(); - return (str().substr(0, parent_base.length()) == parent_base); -} - -bool -Path::is_parent_of(const Path& child) const -{ - return child.is_child_of(*this); -} - -Path -Path::lca(const Path& a, const Path& b) -{ - const size_t len = std::min(a.length(), b.length()); - - size_t last_slash = 0; - for (size_t i = 0; i < len; ++i) { - if (a[i] == '/' && b[i] == '/') { - last_slash = i; - } - if (a[i] != b[i]) { - break; - } - } - - if (last_slash <= 1) { - return Path("/"); - } - return Path(a.str().substr(0, last_slash)); -} - -} // namespace Raul diff --git a/src/Symbol.cpp b/src/Symbol.cpp deleted file mode 100644 index 4fe049e..0000000 --- a/src/Symbol.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard <http://drobilla.net> - - Raul is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or any later version. - - Raul is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Raul. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <string> - -#include "raul/Symbol.hpp" -#include "raul/Path.hpp" - -using std::string; - -namespace Raul { - -bool -Symbol::is_valid(const std::basic_string<char>& symbol) -{ - if (symbol.length() == 0) - return false; - - // Slash not allowed - if (symbol.find("/") != string::npos) - return false; - - // All characters must be _, a-z, A-Z, 0-9 - for (size_t i=0; i < symbol.length(); ++i) - if (symbol[i] != '_' - && (symbol[i] < 'a' || symbol[i] > 'z') - && (symbol[i] < 'A' || symbol[i] > 'Z') - && (symbol[i] < '0' || symbol[i] > '9') ) - return false; - - // First character must not be a number - if (std::isdigit(symbol[0])) - return false; - - return true; -} - -/** Convert a string to a valid symbol. - * - * This will make a best effort at turning @a str into a complete, valid - * Symbol, and will always return one. - */ -Raul::Symbol -Symbol::symbolify(const std::basic_string<char>& str) -{ - string symbol = str; - - Path::replace_invalid_chars(symbol, 0, true); - - return Raul::Symbol(symbol.empty() ? "_" : symbol); -} - -} // namespace Raul - diff --git a/test/path_test.cpp b/test/path_test.cpp index f267595..1bdd99e 100644 --- a/test/path_test.cpp +++ b/test/path_test.cpp @@ -14,8 +14,9 @@ along with Raul. If not, see <http://www.gnu.org/licenses/>. */ +#include <cstring> #include <iostream> -#include <list> + #include "raul/log.hpp" #include "raul/Path.hpp" @@ -31,23 +32,6 @@ main() return 1; \ } } while (0) - list<string> names; - names.push_back("Dry/Wet Balance"); - names.push_back("foo+1bar(baz)"); - names.push_back("ThisCRAR"); - names.push_back("NAME"); - names.push_back("thing with a bunch of spaces"); - names.push_back("thing-with-a-bunch-of-dashes"); - names.push_back("CamelCaseABC"); - names.push_back("Signal Level [dB]"); - names.push_back("Gain dB"); - names.push_back("Dry/Wet Balance"); - names.push_back("Phaser1 - Similar to CSound's phaser1 by Sean Costello"); - - for (list<string>::iterator i = names.begin(); i != names.end(); ++i) { - CHECK(strcmp(Symbol::symbolify(*i).c_str(), "")); - } - CHECK(Path("/foo/bar").parent() == Path("/foo")); CHECK(Path("/foo").parent() == Path("/")); CHECK(Path("/").parent() == Path("/")); @@ -68,13 +52,13 @@ main() CHECK(Path::is_valid("/")); CHECK(!Path::is_valid("/foo/3foo/bar")); - CHECK(Path::descendant_comparator("/", "/foo")); - CHECK(Path::descendant_comparator("/foo", "/foo/bar")); - CHECK(Path::descendant_comparator("/foo", "/foo")); - CHECK(Path::descendant_comparator("/", "/")); - CHECK(!Path::descendant_comparator("/baz", "/")); - CHECK(!Path::descendant_comparator("/foo", "/bar")); - CHECK(!Path::descendant_comparator("/foo/bar", "/foo")); + CHECK(Path::descendant_comparator(Path("/"), Path("/foo"))); + CHECK(Path::descendant_comparator(Path("/foo"), Path("/foo/bar"))); + CHECK(Path::descendant_comparator(Path("/foo"), Path("/foo"))); + CHECK(Path::descendant_comparator(Path("/"), Path("/"))); + CHECK(!Path::descendant_comparator(Path("/baz"), Path("/"))); + CHECK(!Path::descendant_comparator(Path("/foo"), Path("/bar"))); + CHECK(!Path::descendant_comparator(Path("/foo/bar"), Path("/foo"))); CHECK(!Symbol::is_valid("")); CHECK(!Symbol::is_valid("/I/have/slashes")); @@ -82,5 +66,35 @@ main() CHECK(!Symbol::is_valid("0illegalleadingdigit")); CHECK(strcmp(Symbol::symbolify("").c_str(), "")); + CHECK(Path("/foo").child(Symbol("bar")) == "/foo/bar"); + CHECK(Path("/foo").child(Path("/bar/baz")) == "/foo/bar/baz"); + CHECK(Path("/foo").child(Path("/")) == "/foo"); + + CHECK(!strcmp(Path("/foo").symbol(), "foo")); + CHECK(!strcmp(Path("/foo/bar").symbol(), "bar")); + CHECK(!strcmp(Path("/").symbol(), "")); + + Path original(std::string("/foo/bar")); + Path copy(original); + CHECK(original == copy); + + bool valid = true; + try { + Path path("/ends/in/slash/"); + } catch (const Path::BadPath& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + CHECK(!valid); + + valid = true; + try { + Path path(std::string("/has//double/slash")); + } catch (const Path::BadPath& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + CHECK(!valid); + return 0; } diff --git a/test/symbol_test.cpp b/test/symbol_test.cpp new file mode 100644 index 0000000..9726001 --- /dev/null +++ b/test/symbol_test.cpp @@ -0,0 +1,77 @@ +/* + This file is part of Raul. + Copyright 2007-2012 David Robillard <http://drobilla.net> + + Raul is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or any later version. + + Raul is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Raul. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <cstring> +#include <iostream> +#include <list> + +#include "raul/log.hpp" +#include "raul/Path.hpp" + +using namespace std; +using namespace Raul; + +int +main() +{ +#define CHECK(cond) \ + do { if (!(cond)) { \ + error << "Test failed: " << (cond) << endl; \ + return 1; \ + } } while (0) + + list<string> names; + names.push_back("Dry/Wet Balance"); + names.push_back("foo+1bar(baz)"); + names.push_back("ThisCRAR"); + names.push_back("NAME"); + names.push_back("thing with a bunch of spaces"); + names.push_back("thing-with-a-bunch-of-dashes"); + names.push_back("CamelCaseABC"); + names.push_back("Signal Level [dB]"); + names.push_back("Gain dB"); + names.push_back("Dry/Wet Balance"); + names.push_back("Phaser1 - Similar to CSound's phaser1 by Sean Costello"); + names.push_back(""); + + for (list<string>::iterator i = names.begin(); i != names.end(); ++i) { + CHECK(!Symbol::symbolify(*i).empty()); + } + + Symbol original("sym"); + Symbol copy(original); + CHECK(original == copy); + + bool valid = true; + try { + Symbol symbol("0startswithdigit"); + } catch (const Symbol::BadSymbol& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + CHECK(!valid); + + valid = true; + try { + Symbol symbol(std::string("invalid/symbol")); + } catch (const Symbol::BadSymbol& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + CHECK(!valid); + + return 0; +} diff --git a/test/table_test.cpp b/test/table_test.cpp index 70ab5b1..f372d7c 100644 --- a/test/table_test.cpp +++ b/test/table_test.cpp @@ -14,12 +14,13 @@ along with Raul. If not, see <http://www.gnu.org/licenses/>. */ -#include <string> +#include <cstring> #include <iostream> -#include <utility> #include <map> #include <set> +#include <string> #include <sys/time.h> +#include <utility> #include "raul/log.hpp" #include "raul/PathTable.hpp" diff --git a/test/uri_test.cpp b/test/uri_test.cpp new file mode 100644 index 0000000..d3b9a99 --- /dev/null +++ b/test/uri_test.cpp @@ -0,0 +1,61 @@ +/* + This file is part of Raul. + Copyright 2007-2012 David Robillard <http://drobilla.net> + + Raul is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or any later version. + + Raul is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Raul. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <iostream> + +#include "raul/log.hpp" +#include "raul/URI.hpp" + +using namespace std; +using namespace Raul; + +int +main() +{ +#define CHECK(cond) \ + do { if (!(cond)) { \ + error << "Test failed: " << (cond) << endl; \ + assert(0); \ + return 1; \ + } } while (0) + + CHECK(URI("http://example.org").scheme() == "http"); + CHECK(URI("svn+ssh://example.org/").scheme() == "svn+ssh"); + CHECK(URI("osc.udp://example.org/").scheme() == "osc.udp"); + CHECK(URI("weird-scheme://example.org/").scheme() == "weird-scheme"); + + URI original(std::string("http://example.org")); + URI copy(original); + CHECK(original == copy); + + bool valid = true; + try { + URI uri("not/a/uri"); + } catch (const URI::BadURI& e) { + valid = false; + } + CHECK(!valid); + + valid = true; + try { + URI uri(std::string("/this/is/a/path")); + } catch (const URI::BadURI& e) { + valid = false; + } + CHECK(!valid); + + return 0; +} @@ -51,10 +51,6 @@ def configure(conf): autowaf.configure(conf) conf.line_just = 40 autowaf.display_header('Raul Configuration') - autowaf.check_pkg(conf, 'glib-2.0', atleast_version='2.2', - uselib_store='GLIB', mandatory=True) - autowaf.check_pkg(conf, 'gthread-2.0', atleast_version='2.14.0', - uselib_store='GTHREAD', mandatory=True) if Options.platform == 'darwin': conf.check(framework_name='CoreServices') @@ -95,9 +91,11 @@ tests = ''' test/queue_test test/ringbuffer_test test/sem_test + test/symbol_test test/table_test test/thread_test test/time_test + test/uri_test ''' def build(bld): @@ -115,8 +113,6 @@ def build(bld): lib_source = ''' src/Configuration.cpp src/Maid.cpp - src/Path.cpp - src/Symbol.cpp src/Thread.cpp src/log.cpp ''' @@ -144,7 +140,7 @@ def build(bld): set_defines(obj); if bld.env.BUILD_TESTS: - test_libs = [] + test_libs = ['pthread', 'rt'] test_cxxflags = [] if bld.is_defined('HAVE_GCOV'): test_libs += ['gcov'] |