summaryrefslogtreecommitdiffstats
path: root/include/raul
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-11-12 01:11:11 +0100
committerDavid Robillard <d@drobilla.net>2020-11-12 01:47:40 +0100
commitbf9190ef628c1aa04791af1bd7cd4905e9c24658 (patch)
tree56114c93a8ec689cd15497059958dad03a1ee2ce /include/raul
parent496e70e420811c7d744a8bcc44a2ac1b51b676b5 (diff)
downloadraul-bf9190ef628c1aa04791af1bd7cd4905e9c24658.tar.gz
raul-bf9190ef628c1aa04791af1bd7cd4905e9c24658.tar.bz2
raul-bf9190ef628c1aa04791af1bd7cd4905e9c24658.zip
Move includes to a conventional include directory
Diffstat (limited to 'include/raul')
-rw-r--r--include/raul/Array.hpp160
-rw-r--r--include/raul/Deletable.hpp37
-rw-r--r--include/raul/DoubleBuffer.hpp94
-rw-r--r--include/raul/Exception.hpp40
-rw-r--r--include/raul/Maid.hpp148
-rw-r--r--include/raul/Noncopyable.hpp34
-rw-r--r--include/raul/Path.hpp207
-rw-r--r--include/raul/Process.hpp87
-rw-r--r--include/raul/RingBuffer.hpp219
-rw-r--r--include/raul/Semaphore.hpp270
-rw-r--r--include/raul/Socket.hpp262
-rw-r--r--include/raul/Symbol.hpp130
-rw-r--r--include/raul/TimeSlice.hpp157
-rw-r--r--include/raul/TimeStamp.hpp236
14 files changed, 2081 insertions, 0 deletions
diff --git a/include/raul/Array.hpp b/include/raul/Array.hpp
new file mode 100644
index 0000000..ee50470
--- /dev/null
+++ b/include/raul/Array.hpp
@@ -0,0 +1,160 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2013 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/>.
+*/
+
+#ifndef RAUL_ARRAY_HPP
+#define RAUL_ARRAY_HPP
+
+#include "raul/Maid.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <memory>
+
+namespace Raul {
+
+/** A disposable array with size.
+ *
+ * \ingroup raul
+ */
+template <class T>
+class Array : public Maid::Disposable
+{
+public:
+ explicit Array(size_t size = 0)
+ : Maid::Disposable()
+ , _size(size)
+ , _elems(size ? new T[size] : nullptr)
+ {
+ }
+
+ Array(size_t size, T initial_value)
+ : Maid::Disposable()
+ , _size(size)
+ , _elems(size ? new T[size] : nullptr)
+ {
+ if (size > 0) {
+ for (size_t i = 0; i < size; ++i) {
+ _elems[i] = initial_value;
+ }
+ }
+ }
+
+ Array(const Array<T>& array)
+ : Maid::Disposable()
+ , _size(array._size)
+ , _elems(_size ? new T[_size] : nullptr)
+ {
+ for (size_t i = 0; i < _size; ++i) {
+ _elems[i] = array._elems[i];
+ }
+ }
+
+ ~Array() override = default;
+
+ Array<T>& operator=(const Array<T>& array)
+ {
+ if (&array == this) {
+ return *this;
+ }
+
+ _size = array._size;
+ _elems = _size ? new T[_size] : nullptr;
+
+ for (size_t i = 0; i < _size; ++i) {
+ _elems[i] = array._elems[i];
+ }
+ }
+
+ Array(Array<T>&& array) noexcept
+ : _size(array._size)
+ , _elems(std::move(array._elems))
+ {
+ }
+
+ Array<T>& operator=(Array<T>&& array) noexcept
+ {
+ _size = array._size;
+ _elems = std::move(array._elems);
+ return *this;
+ }
+
+ Array(size_t size, const Array<T>& contents)
+ : _size(size)
+ , _elems(size ? new T[size] : nullptr)
+ {
+ assert(contents.size() >= size);
+ for (size_t i = 0; i < std::min(size, contents.size()); ++i) {
+ _elems[i] = contents[i];
+ }
+ }
+
+ Array(size_t size, const Array<T>& contents, T initial_value = T())
+ : _size(size)
+ , _elems(size ? new T[size] : nullptr)
+ {
+ const size_t end = std::min(size, contents.size());
+ for (size_t i = 0; i < end; ++i) {
+ _elems[i] = contents[i];
+ }
+ for (size_t i = end; i < size; ++i) {
+ _elems[i] = initial_value;
+ }
+ }
+
+ virtual void alloc(size_t num_elems) {
+ _size = num_elems;
+
+ if (num_elems > 0) {
+ _elems = std::unique_ptr<T[]>(new T[num_elems]);
+ } else {
+ _elems.reset();
+ }
+ }
+
+ virtual void alloc(size_t num_elems, T initial_value) {
+ _size = num_elems;
+
+ if (num_elems > 0) {
+ _elems = std::unique_ptr<T[]>(new T[num_elems]);
+ for (size_t i = 0; i < _size; ++i) {
+ _elems[i] = initial_value;
+ }
+ } else {
+ _elems.reset();
+ }
+ }
+
+ inline size_t size() const { return _size; }
+
+ inline T& operator[](size_t i) const {
+ assert(i < _size);
+ return _elems[i];
+ }
+
+ inline T& at(size_t i) const {
+ assert(i < _size);
+ return _elems[i];
+ }
+
+private:
+ size_t _size;
+ std::unique_ptr<T[]> _elems;
+};
+
+} // namespace Raul
+
+#endif // RAUL_ARRAY_HPP
diff --git a/include/raul/Deletable.hpp b/include/raul/Deletable.hpp
new file mode 100644
index 0000000..9b80d54
--- /dev/null
+++ b/include/raul/Deletable.hpp
@@ -0,0 +1,37 @@
+
+/*
+ This file is part of Raul.
+ Copyright 2007-2013 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/>.
+*/
+
+#ifndef RAUL_DELETABLE_HPP
+#define RAUL_DELETABLE_HPP
+
+namespace Raul {
+
+/** Something with a virtual destructor.
+ *
+ * \ingroup raul
+ */
+class Deletable
+{
+public:
+ Deletable() = default;
+
+ virtual ~Deletable() = default;
+};
+
+} // namespace Raul
+
+#endif // RAUL_DELETABLE_HPP
diff --git a/include/raul/DoubleBuffer.hpp b/include/raul/DoubleBuffer.hpp
new file mode 100644
index 0000000..73b5565
--- /dev/null
+++ b/include/raul/DoubleBuffer.hpp
@@ -0,0 +1,94 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2013 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/>.
+*/
+
+#ifndef RAUL_DOUBLEBUFFER_HPP
+#define RAUL_DOUBLEBUFFER_HPP
+
+#include <atomic>
+#include <utility>
+
+namespace Raul {
+
+/** Double buffer.
+ *
+ * Can be thought of as a wrapper class to make a non-atomic type atomically
+ * settable (with no locking).
+ *
+ * Read/Write realtime safe, many writers safe - but set calls may fail.
+ *
+ * Space: 2*sizeof(T) + sizeof(int) + sizeof(void*)
+ * \ingroup raul
+ */
+template<typename T>
+class DoubleBuffer {
+public:
+ explicit DoubleBuffer(T val)
+ : _state(State::READ_WRITE)
+ {
+ _vals[0] = std::move(val);
+ }
+
+ DoubleBuffer(const DoubleBuffer&) = delete;
+ DoubleBuffer& operator=(const DoubleBuffer&) = delete;
+
+ inline const T& get() const {
+ switch (_state.load(std::memory_order_acquire)) {
+ case State::READ_WRITE:
+ case State::READ_LOCK:
+ return _vals[0];
+ case State::WRITE_READ:
+ case State::LOCK_READ:
+ break;
+ }
+ return _vals[1];
+ }
+
+ inline bool set(T new_val) {
+ if (transition(State::READ_WRITE, State::READ_LOCK)) {
+ // Locked _vals[1] for writing
+ _vals[1] = std::move(new_val);
+ _state.store(State::WRITE_READ, std::memory_order_release);
+ return true;
+ } else if (transition(State::WRITE_READ, State::LOCK_READ)) {
+ // Locked _vals[0] for writing
+ _vals[0] = std::move(new_val);
+ _state.store(State::READ_WRITE, std::memory_order_release);
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+ enum class State {
+ READ_WRITE, ///< Read vals[0], Write vals[1]
+ READ_LOCK, ///< Read vals[0], Lock vals[1]
+ WRITE_READ, ///< Write vals[0], Read vals[1]
+ LOCK_READ ///< Lock vals[0], Read vals[1]
+ };
+
+ bool transition(State from, const State to) {
+ return _state.compare_exchange_strong(
+ from, to, std::memory_order_release, std::memory_order_relaxed);
+ }
+
+ std::atomic<State> _state;
+ T _vals[2];
+};
+
+} // namespace Raul
+
+#endif // RAUL_DOUBLEBUFFER_HPP
diff --git a/include/raul/Exception.hpp b/include/raul/Exception.hpp
new file mode 100644
index 0000000..da4a0e0
--- /dev/null
+++ b/include/raul/Exception.hpp
@@ -0,0 +1,40 @@
+/*
+ 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>
+#include <utility>
+
+#ifndef RAUL_EXCEPTION_HPP
+#define RAUL_EXCEPTION_HPP
+
+namespace Raul {
+
+/** An exception (unexpected error). */
+class Exception : public std::exception {
+public:
+ const char* what() const noexcept override { return _what.c_str(); }
+
+protected:
+ explicit Exception(std::string what) : _what(std::move(what)) {}
+
+private:
+ const std::string _what;
+};
+
+} // namespace Raul
+
+#endif // RAUL_EXCEPTION_HPP
diff --git a/include/raul/Maid.hpp b/include/raul/Maid.hpp
new file mode 100644
index 0000000..1479f71
--- /dev/null
+++ b/include/raul/Maid.hpp
@@ -0,0 +1,148 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2017 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/>.
+*/
+
+#ifndef RAUL_MAID_HPP
+#define RAUL_MAID_HPP
+
+#include "raul/Deletable.hpp"
+#include "raul/Noncopyable.hpp"
+
+#include <atomic>
+#include <memory>
+
+namespace Raul {
+
+/** Explicit garbage-collector.
+ *
+ * This allows objects to be disposed of in a real-time thread, but actually
+ * deleted later by another thread which calls cleanup(). Disposable objects
+ * may be explicitly disposed by calling dispose(), or automatically managed
+ * with a managed_ptr which can safely be dropped in any thread, including
+ * real-time threads.
+ *
+ * \ingroup raul
+ */
+class Maid : public Noncopyable
+{
+public:
+ /** An object that can be disposed via Maid::dispose(). */
+ class Disposable : public Deletable {
+ public:
+ Disposable() = default;
+ Disposable(const Disposable&) = delete;
+ Disposable& operator=(const Disposable&) = delete;
+
+ private:
+ friend class Maid;
+ Disposable* _maid_next{};
+ };
+
+ /** Disposable wrapper for any type. */
+ template<typename T>
+ class Managed : public Raul::Maid::Disposable, public T
+ {
+ public:
+ template<typename... Args>
+ explicit Managed(Args&&... args)
+ : T(std::forward<Args>(args)...)
+ {}
+ };
+
+ /** Deleter for Disposable objects. */
+ template<typename T>
+ class Disposer {
+ public:
+ explicit Disposer(Maid* maid) : _maid(maid) {}
+
+ Disposer() = default;
+
+ void operator()(T* obj) {
+ if (_maid) { _maid->dispose(obj); }
+ }
+
+ private:
+ Maid* _maid{nullptr};
+ };
+
+ /** A managed pointer that automatically disposes of its contents.
+ *
+ * This is a unique_ptr so that it is possible to statically verify that
+ * code is real-time safe.
+ */
+ template<typename T> using managed_ptr = std::unique_ptr<T, Disposer<T>>;
+
+ Maid() : _disposed(nullptr) {}
+
+ inline ~Maid() { cleanup(); }
+
+ /** Return false iff there is currently no garbage. */
+ inline bool empty() const {
+ return !_disposed.load(std::memory_order_relaxed);
+ }
+
+ /** Enqueue an object for deletion when cleanup() is called next.
+ *
+ * This is thread-safe, and real-time safe assuming reasonably low
+ * contention.
+ */
+ inline void dispose(Disposable* obj) {
+ if (obj) {
+ // Atomically add obj to the head of the disposed list
+ do {
+ obj->_maid_next = _disposed.load(std::memory_order_relaxed);
+ } while (!_disposed.compare_exchange_weak(
+ obj->_maid_next, obj,
+ std::memory_order_release,
+ std::memory_order_relaxed));
+ }
+ }
+
+ /** Delete all disposed objects immediately.
+ *
+ * Obviously not real-time safe, but may be called while other threads are
+ * calling dispose().
+ */
+ inline void cleanup() {
+ // Atomically get the head of the disposed list
+ Disposable* const disposed = _disposed.exchange(
+ nullptr, std::memory_order_acquire);
+
+ // Free the disposed list
+ for (Disposable* obj = disposed; obj;) {
+ Disposable* const next = obj->_maid_next;
+ delete obj;
+ obj = next;
+ }
+ }
+
+ /** Make a unique_ptr that will dispose its object when dropped. */
+ template<class T, class... Args>
+ managed_ptr<T> make_managed(Args&&... args) {
+ return std::unique_ptr<T, Disposer<T> >(
+ new T(std::forward<Args>(args)...),
+ Disposer<T>(this));
+ }
+
+private:
+ std::atomic<Disposable*> _disposed;
+};
+
+template<typename T>
+using managed_ptr = Maid::managed_ptr<T>;
+
+} // namespace Raul
+
+#endif // RAUL_MAID_HPP
diff --git a/include/raul/Noncopyable.hpp b/include/raul/Noncopyable.hpp
new file mode 100644
index 0000000..876cee8
--- /dev/null
+++ b/include/raul/Noncopyable.hpp
@@ -0,0 +1,34 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2013 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/>.
+*/
+
+#ifndef RAUL_NONCOPYABLE_HPP
+#define RAUL_NONCOPYABLE_HPP
+
+namespace Raul {
+
+class Noncopyable {
+public:
+ Noncopyable(const Noncopyable&) = delete;
+ const Noncopyable& operator=(const Noncopyable&) = delete;
+
+protected:
+ Noncopyable() = default;
+ ~Noncopyable() = default;
+};
+
+} // namespace Raul
+
+#endif // RAUL_NONCOPYABLE_HPP
diff --git a/include/raul/Path.hpp b/include/raul/Path.hpp
new file mode 100644
index 0000000..6237ddd
--- /dev/null
+++ b/include/raul/Path.hpp
@@ -0,0 +1,207 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2014 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/>.
+*/
+
+#ifndef RAUL_PATH_HPP
+#define RAUL_PATH_HPP
+
+#include "raul/Exception.hpp"
+#include "raul/Symbol.hpp"
+
+#include <algorithm>
+#include <string>
+
+namespace Raul {
+
+/** A restricted path of Symbols separated by, and beginning with, "/".
+ *
+ * A Path never ends with a "/", except for the root Path "/", which is the
+ * only valid single-character Path.
+ *
+ * @ingroup raul
+ */
+class Path : public std::basic_string<char> {
+public:
+ /** Attempt to construct an invalid Path. */
+ class BadPath : public Exception {
+ public:
+ explicit BadPath(const std::string& path) : Exception(path) {}
+ };
+
+ /** Construct an uninitialzed path, because the STL is annoying. */
+ Path() : std::basic_string<char>("/") {}
+
+ /** Construct a Path from a C++ string.
+ *
+ * This will throw an exception if `path` is invalid. To avoid this, use
+ * is_valid() first to check.
+ */
+ explicit Path(const std::basic_string<char>& path)
+ : std::basic_string<char>(path)
+ {
+ if (!is_valid(path)) {
+ throw BadPath(path);
+ }
+ }
+
+ /** Construct a Path from a C string.
+ *
+ * This will throw an exception if `path` is invalid. To avoid this, use
+ * is_valid() first to check.
+ */
+ explicit Path(const char* path)
+ : std::basic_string<char>(path)
+ {
+ if (!is_valid(path)) {
+ throw BadPath(path);
+ }
+ }
+
+ /** Copy a Path.
+ *
+ * Note this is faster than constructing a Path from another Path's string
+ * since validation is unnecessary.
+ */
+ Path(const Path& path) = default;
+
+ /** Return true iff `c` is a valid Path character. */
+ static inline bool is_valid_char(char c) {
+ return c == '/' || Symbol::is_valid_char(c);
+ }
+
+ /** Return true iff `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 '/'
+ }
+
+ if (str != "/" && *str.rbegin() == '/') {
+ return false; // Must not end with '/' except for the root
+ }
+
+ 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)
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /** Return true iff this path is the root path ("/"). */
+ inline bool is_root() const { return *this == "/"; }
+
+ /** Return true iff this path is a child of `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 true iff this path is a parent of `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 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 size_t last_sep = rfind('/');
+ if (last_sep != std::string::npos) {
+ return c_str() + last_sep + 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 (is_root()) {
+ return *this;
+ } else {
+ 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));
+ }
+ }
+
+ /** 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 a direct child Path of this Path with the given Symbol. */
+ inline Path child(const Raul::Symbol& symbol) const {
+ return Path(base() + symbol.c_str());
+ }
+
+ /** 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 std::string base() const {
+ if (is_root()) {
+ return *this;
+ } else {
+ return *this + '/';
+ }
+ }
+
+ /** 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 iff `child` is equal to, or a descendant of `parent`. */
+ static inline bool descendant_comparator(const Path& parent,
+ const Path& child) {
+ return (child == parent || child.is_child_of(parent));
+ }
+};
+
+} // namespace Raul
+
+#endif // RAUL_PATH_HPP
diff --git a/include/raul/Process.hpp b/include/raul/Process.hpp
new file mode 100644
index 0000000..717a900
--- /dev/null
+++ b/include/raul/Process.hpp
@@ -0,0 +1,87 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2015 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/>.
+*/
+
+#ifndef RAUL_PROCESS_HPP
+#define RAUL_PROCESS_HPP
+
+#include "raul/Noncopyable.hpp"
+
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace Raul {
+
+/** A child process.
+ *
+ * \ingroup raul
+ */
+class Process : Noncopyable
+{
+public:
+ /** Launch a sub process.
+ *
+ * @param argv List of arguments, where argv[0] is the command name.
+ * @return True on success.
+ */
+ static bool launch(const char* const argv[]) {
+ // Use the same double fork() trick as JACK to prevent zombie children
+ const int child = fork();
+
+ if (child == 0) {
+ // (in child)
+
+ // Close all nonstandard file descriptors
+ struct rlimit max_fds{};
+ getrlimit(RLIMIT_NOFILE, &max_fds);
+ for (rlim_t fd = 3; fd < max_fds.rlim_cur; ++fd) {
+ close(static_cast<int>(fd));
+ }
+
+ // Fork child
+ const int grandchild = fork();
+ switch (grandchild) {
+ case 0:
+ // (in grandchild)
+ setsid();
+ execvp(argv[0], const_cast<char*const*>(argv));
+ _exit(-1);
+
+ case -1:
+ // Fork failed, there is no grandchild
+ _exit(-1);
+
+ default:
+ // Fork succeeded, return grandchild PID
+ _exit(grandchild);
+ }
+ } else if (child < 0) {
+ // Fork failed, there is no child
+ return false;
+ }
+
+ return true;
+ }
+
+private:
+ Process() = default;
+};
+
+} // namespace Raul
+
+#endif // RAUL_PROCESS_HPP
diff --git a/include/raul/RingBuffer.hpp b/include/raul/RingBuffer.hpp
new file mode 100644
index 0000000..a7bbfb7
--- /dev/null
+++ b/include/raul/RingBuffer.hpp
@@ -0,0 +1,219 @@
+/*
+ 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/>.
+*/
+
+#ifndef RAUL_RINGBUFFER_HPP
+#define RAUL_RINGBUFFER_HPP
+
+#include "raul/Noncopyable.hpp"
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+
+namespace Raul {
+
+/**
+ A lock-free RingBuffer.
+
+ Thread-safe with a single reader and single writer, and real-time safe
+ on both ends.
+
+ @ingroup raul
+*/
+class RingBuffer : public Noncopyable {
+public:
+ /**
+ Create a new RingBuffer.
+ @param size Size in bytes (note this may be rounded up).
+ */
+ explicit RingBuffer(uint32_t size)
+ : _write_head(0)
+ , _read_head(0)
+ , _size(next_power_of_two(size))
+ , _size_mask(_size - 1)
+ , _buf(new char[_size])
+ {
+ assert(read_space() == 0);
+ assert(write_space() == _size - 1);
+ }
+
+ /**
+ Destroy a RingBuffer.
+ */
+ inline ~RingBuffer() = default;
+
+ /**
+ Reset (empty) the RingBuffer.
+
+ This method is NOT thread-safe, it may only be called when there are no
+ readers or writers.
+ */
+ inline void reset() {
+ _write_head = 0;
+ _read_head = 0;
+ }
+
+ /**
+ Return the number of bytes of space available for reading.
+ */
+ inline uint32_t read_space() const {
+ return read_space_internal(_read_head, _write_head);
+ }
+
+ /**
+ Return the number of bytes of space available for writing.
+ */
+ inline uint32_t write_space() const {
+ return write_space_internal(_read_head, _write_head);
+ }
+
+ /**
+ Return the capacity (i.e. total write space when empty).
+ */
+ inline uint32_t capacity() const { return _size - 1; }
+
+ /**
+ Read from the RingBuffer without advancing the read head.
+ */
+ inline uint32_t peek(uint32_t size, void* dst) {
+ return peek_internal(_read_head, _write_head, size, dst);
+ }
+
+ /**
+ Read from the RingBuffer and advance the read head.
+ */
+ inline uint32_t read(uint32_t size, void* dst) {
+ const uint32_t r = _read_head;
+ const uint32_t w = _write_head;
+
+ if (peek_internal(r, w, size, dst)) {
+ std::atomic_thread_fence(std::memory_order_acquire);
+ _read_head = (r + size) & _size_mask;
+ return size;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ Skip data in the RingBuffer (advance read head without reading).
+ */
+ inline uint32_t skip(uint32_t size) {
+ const uint32_t r = _read_head;
+ const uint32_t w = _write_head;
+ if (read_space_internal(r, w) < size) {
+ return 0;
+ }
+
+ std::atomic_thread_fence(std::memory_order_acquire);
+ _read_head = (r + size) & _size_mask;
+ return size;
+ }
+
+ /**
+ Write data to the RingBuffer.
+ */
+ inline uint32_t write(uint32_t size, const void* src) {
+ const uint32_t r = _read_head;
+ const uint32_t w = _write_head;
+ if (write_space_internal(r, w) < size) {
+ return 0;
+ }
+
+ if (w + size <= _size) {
+ memcpy(&_buf[w], src, size);
+ std::atomic_thread_fence(std::memory_order_release);
+ _write_head = (w + size) & _size_mask;
+ } else {
+ const uint32_t this_size = _size - w;
+ assert(this_size < size);
+ assert(w + this_size <= _size);
+ memcpy(&_buf[w], src, this_size);
+ memcpy(&_buf[0],
+ static_cast<const char*>(src) + this_size,
+ size - this_size);
+ std::atomic_thread_fence(std::memory_order_release);
+ _write_head = size - this_size;
+ }
+
+ return size;
+ }
+
+private:
+ static inline uint32_t next_power_of_two(uint32_t size) {
+ // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+ size--;
+ size |= size >> 1U;
+ size |= size >> 2U;
+ size |= size >> 4U;
+ size |= size >> 8U;
+ size |= size >> 16U;
+ size++;
+ return size;
+ }
+
+ inline uint32_t write_space_internal(uint32_t r, uint32_t w) const {
+ if (r == w) {
+ return _size - 1;
+ } else if (r < w) {
+ return ((r - w + _size) & _size_mask) - 1;
+ } else {
+ return (r - w) - 1;
+ }
+ }
+
+ inline uint32_t read_space_internal(uint32_t r, uint32_t w) const {
+ if (r < w) {
+ return w - r;
+ } else {
+ return (w - r + _size) & _size_mask;
+ }
+ }
+
+ inline uint32_t peek_internal(uint32_t r, uint32_t w,
+ uint32_t size, void* dst) const {
+ if (read_space_internal(r, w) < size) {
+ return 0;
+ }
+
+ if (r + size < _size) {
+ memcpy(dst, &_buf[r], size);
+ } else {
+ const uint32_t first_size = _size - r;
+ memcpy(dst, &_buf[r], first_size);
+ memcpy(static_cast<char*>(dst) + first_size,
+ &_buf[0],
+ size - first_size);
+ }
+
+ return size;
+ }
+
+ mutable uint32_t _write_head; ///< Read index into _buf
+ mutable uint32_t _read_head; ///< Write index into _buf
+
+ const uint32_t _size; ///< Size (capacity) in bytes
+ const uint32_t _size_mask; ///< Mask for fast modulo
+
+ const std::unique_ptr<char[]> _buf; ///< Contents
+};
+
+} // namespace Raul
+
+#endif // RAUL_RINGBUFFER_HPP
diff --git a/include/raul/Semaphore.hpp b/include/raul/Semaphore.hpp
new file mode 100644
index 0000000..ee2325f
--- /dev/null
+++ b/include/raul/Semaphore.hpp
@@ -0,0 +1,270 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2014 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/>.
+*/
+
+#ifndef RAUL_SEMAPHORE_HPP
+#define RAUL_SEMAPHORE_HPP
+
+#ifdef __APPLE__
+# include <mach/mach.h>
+#elif defined(_WIN32)
+# define NOMINMAX
+# include <windows.h>
+#else
+# include <cerrno>
+# include <ctime>
+# include <semaphore.h>
+#endif
+
+#include <chrono>
+#include <stdexcept>
+
+namespace Raul {
+
+/**
+ Unnamed (process local) counting semaphore.
+
+ The civilized person's synchronisation primitive. A counting semaphore is
+ an integer which is always non-negative, so, an attempted decrement (or
+ "wait") will block if the value is 0, until another thread does an increment
+ (or "post").
+
+ At least on Lignux, the main advantage of this is that it is fast and the
+ only safe way to reliably signal from a real-time audio thread. The
+ counting semantics also complement ringbuffers of events nicely.
+*/
+class Semaphore
+{
+public:
+ /**
+ Create a new semaphore.
+
+ Chances are you want 1 wait() per 1 post(), an initial value of 0.
+ */
+ explicit Semaphore(unsigned initial)
+ : _sem()
+ {
+ if (!init(initial)) {
+ throw std::runtime_error("Failed to create semaphore");
+ }
+ }
+
+ inline Semaphore(const Semaphore&) = delete;
+ inline Semaphore& operator=(const Semaphore&) = delete;
+
+ inline ~Semaphore() {
+ destroy();
+ }
+
+ /** Destroy and reset to a new initial value. */
+ inline void reset(unsigned initial) {
+ destroy();
+ init(initial);
+ }
+
+ /** Post/Increment/Signal */
+ inline void post();
+
+ /** Wait/Decrement. Return false on error. */
+ inline bool wait();
+
+ /** Attempt Wait/Decrement. Return true iff decremented. */
+ inline bool try_wait();
+
+ /** Wait for at most `ms` milliseconds. Return true iff decremented. */
+ template<class Rep, class Period>
+ inline bool timed_wait(const std::chrono::duration<Rep, Period>& wait);
+
+private:
+ inline bool init(unsigned initial);
+ inline void destroy();
+
+#if defined(__APPLE__)
+ semaphore_t _sem; // sem_t is a worthless broken mess on OSX
+#elif defined(_WIN32)
+ HANDLE _sem; // types are overrated anyway
+#else
+ sem_t _sem;
+#endif
+};
+
+#ifdef __APPLE__
+
+inline bool
+Semaphore::init(unsigned initial)
+{
+ if (semaphore_create(mach_task_self(), &_sem, SYNC_POLICY_FIFO, int(initial))) {
+ return false;
+ }
+ return true;
+}
+
+inline void
+Semaphore::destroy()
+{
+ semaphore_destroy(mach_task_self(), _sem);
+}
+
+inline void
+Semaphore::post()
+{
+ semaphore_signal(_sem);
+}
+
+inline bool
+Semaphore::wait()
+{
+ if (semaphore_wait(_sem) != KERN_SUCCESS) {
+ return false;
+ }
+ return true;
+}
+
+inline bool
+Semaphore::try_wait()
+{
+ const mach_timespec_t zero = { 0, 0 };
+ return semaphore_timedwait(_sem, zero) == KERN_SUCCESS;
+}
+
+template<class Rep, class Period>
+inline bool
+Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait)
+{
+ namespace chr = std::chrono;
+
+ const chr::seconds sec(chr::duration_cast<chr::seconds>(wait));
+ const chr::nanoseconds nsec(wait - sec);
+
+ const mach_timespec_t t = { static_cast<unsigned>(sec.count()),
+ static_cast<int>(nsec.count()) };
+ return semaphore_timedwait(_sem, t) == KERN_SUCCESS;
+}
+
+#elif defined(_WIN32)
+
+inline bool
+Semaphore::init(unsigned initial)
+{
+ if (!(_sem = CreateSemaphore(NULL, (LONG)initial, LONG_MAX, NULL))) {
+ return false;
+ }
+ return true;
+}
+
+inline void
+Semaphore::destroy()
+{
+ CloseHandle(_sem);
+}
+
+inline void
+Semaphore::post()
+{
+ ReleaseSemaphore(_sem, 1, NULL);
+}
+
+inline bool
+Semaphore::wait()
+{
+ if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) {
+ return false;
+ }
+ return true;
+}
+
+inline bool
+Semaphore::try_wait()
+{
+ return WaitForSingleObject(_sem, 0) == WAIT_OBJECT_0;
+}
+
+template<class Rep, class Period>
+inline bool
+Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait)
+{
+ namespace chr = std::chrono;
+
+ const chr::milliseconds ms(chr::duration_cast<chr::milliseconds>(wait));
+ return WaitForSingleObject(
+ _sem, static_cast<DWORD>(ms.count())) == WAIT_OBJECT_0;
+}
+
+#else /* !defined(__APPLE__) && !defined(_WIN32) */
+
+inline bool
+Semaphore::init(unsigned initial)
+{
+ return !sem_init(&_sem, 0, initial);
+}
+
+inline void
+Semaphore::destroy()
+{
+ sem_destroy(&_sem);
+}
+
+inline void
+Semaphore::post()
+{
+ sem_post(&_sem);
+}
+
+inline bool
+Semaphore::wait()
+{
+ while (sem_wait(&_sem)) {
+ if (errno != EINTR) {
+ return false; // We are all doomed
+ }
+ /* Otherwise, interrupted (rare/weird), so try again. */
+ }
+
+ return true;
+}
+
+inline bool
+Semaphore::try_wait()
+{
+ return (sem_trywait(&_sem) == 0);
+}
+
+template<class Rep, class Period>
+inline bool
+Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait)
+{
+ namespace chr = std::chrono;
+
+ // Use clock_gettime to ensure sem_timedwait uses the same clock
+ struct timespec time{};
+ clock_gettime(CLOCK_REALTIME, &time);
+
+ const auto now(chr::seconds(time.tv_sec) + chr::nanoseconds(time.tv_nsec));
+ const auto end(now + wait);
+
+ const chr::seconds end_sec(chr::duration_cast<chr::seconds>(end));
+ const chr::nanoseconds end_nsec(end - end_sec);
+
+ const struct timespec ts_end = { static_cast<time_t>(end_sec.count()),
+ static_cast<long>(end_nsec.count()) };
+
+ return (sem_timedwait(&_sem, &ts_end) == 0);
+}
+
+#endif
+
+} // namespace Raul
+
+#endif // RAUL_SEMAPHORE_HPP
diff --git a/include/raul/Socket.hpp b/include/raul/Socket.hpp
new file mode 100644
index 0000000..579a5cd
--- /dev/null
+++ b/include/raul/Socket.hpp
@@ -0,0 +1,262 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2015 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/>.
+*/
+
+#ifndef RAUL_SOCKET_HPP
+#define RAUL_SOCKET_HPP
+
+#include "raul/Noncopyable.hpp"
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <cerrno>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+#include <string>
+
+namespace Raul {
+
+/** A safe and simple interface for UNIX or TCP sockets. */
+class Socket : public Raul::Noncopyable {
+public:
+ enum class Type {
+ UNIX,
+ TCP
+ };
+
+ /** Create a new unbound/unconnected socket of a given type. */
+ explicit Socket(Type t);
+
+ /** Wrap an existing open socket. */
+ Socket(Type t,
+ std::string uri,
+ struct sockaddr* addr,
+ socklen_t addr_len,
+ int fd);
+
+ Socket(const Socket&) = delete;
+ Socket& operator=(const Socket&) = delete;
+
+ ~Socket();
+
+ /** Bind a server socket to an address.
+ * @param uri Address URI, e.g. unix:///tmp/foo, or tcp://hostname:1234.
+ * Use "*" as hostname to listen on all interfaces.
+ * @return True on success.
+ */
+ bool bind(const std::string& uri);
+
+ /** Connect a client socket to a server address.
+ * @param uri Address URI, e.g. unix:///tmp/foo or tcp://somehost:1234
+ * @return True on success.
+ */
+ bool connect(const std::string& uri);
+
+ /** Mark server socket as passive to listen for incoming connections.
+ * @return True on success.
+ */
+ bool listen();
+
+ /** Accept a connection.
+ * @return An new open socket for the connection.
+ */
+ std::shared_ptr<Socket> accept();
+
+ /** Return the file descriptor for the socket. */
+ int fd() const { return _sock; }
+
+ const std::string& uri() const { return _uri; }
+
+ /** Close the socket. */
+ void close();
+
+ /** Shut down the socket.
+ * This terminates any connections associated with the sockets, and will
+ * (unlike close()) cause a poll on the socket to return.
+ */
+ void shutdown();
+
+private:
+ bool set_addr(const std::string& uri);
+
+ std::string _uri;
+ struct sockaddr* _addr;
+ socklen_t _addr_len;
+ Type _type;
+ int _sock;
+};
+
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif
+
+inline
+Socket::Socket(Type t)
+ : _uri(t == Type::UNIX ? "unix:" : "tcp:")
+ , _addr(nullptr)
+ , _addr_len(0)
+ , _type(t)
+ , _sock(-1)
+{
+ switch (t) {
+ case Type::UNIX:
+ _sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ break;
+ case Type::TCP:
+ _sock = socket(AF_INET, SOCK_STREAM, 0);
+ break;
+ }
+}
+
+inline
+Socket::Socket(Type t,
+ std::string uri,
+ struct sockaddr* addr,
+ socklen_t addr_len,
+ int fd)
+ : _uri(std::move(uri))
+ , _addr(addr)
+ , _addr_len(addr_len)
+ , _type(t)
+ , _sock(fd)
+{
+}
+
+inline
+Socket::~Socket()
+{
+ free(_addr);
+ close();
+}
+
+inline bool
+Socket::set_addr(const std::string& uri)
+{
+ free(_addr);
+ if (_type == Type::UNIX && uri.substr(0, strlen("unix://")) == "unix://") {
+ const std::string path = uri.substr(strlen("unix://"));
+ auto* uaddr = static_cast<struct sockaddr_un*>(
+ calloc(1, sizeof(struct sockaddr_un)));
+ uaddr->sun_family = AF_UNIX;
+ strncpy(uaddr->sun_path, path.c_str(), sizeof(uaddr->sun_path) - 1);
+ _uri = uri;
+ _addr = reinterpret_cast<sockaddr*>(uaddr);
+ _addr_len = sizeof(struct sockaddr_un);
+ return true;
+ } else if (_type == Type::TCP && uri.find("://") != std::string::npos) {
+ const std::string authority = uri.substr(uri.find("://") + 3);
+ const size_t port_sep = authority.find(':');
+ if (port_sep == std::string::npos) {
+ return false;
+ }
+
+ std::string host = authority.substr(0, port_sep);
+ const std::string port = authority.substr(port_sep + 1);
+ if (host.empty() || host == "*") {
+ host = "0.0.0.0"; // INADDR_ANY
+ }
+
+ struct addrinfo* ainfo = nullptr;
+ if (getaddrinfo(host.c_str(), port.c_str(), nullptr, &ainfo)) {
+ return false;
+ }
+
+ _uri = uri;
+ _addr = static_cast<struct sockaddr*>(malloc(ainfo->ai_addrlen));
+ _addr_len = ainfo->ai_addrlen;
+ memcpy(_addr, ainfo->ai_addr, ainfo->ai_addrlen);
+ freeaddrinfo(ainfo);
+ return true;
+ }
+ return false;
+}
+
+inline bool
+Socket::bind(const std::string& uri)
+{
+ return set_addr(uri) && ::bind(_sock, _addr, _addr_len) != -1;
+}
+
+inline bool
+Socket::connect(const std::string& uri)
+{
+ return set_addr(uri) && ::connect(_sock, _addr, _addr_len) != -1;
+}
+
+inline bool
+Socket::listen()
+{
+ if (::listen(_sock, 64) == -1) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+inline std::shared_ptr<Socket>
+Socket::accept()
+{
+ socklen_t client_addr_len = _addr_len;
+ auto* const client_addr =
+ static_cast<struct sockaddr*>(calloc(1, client_addr_len));
+
+ const int conn = ::accept(_sock, client_addr, &client_addr_len);
+ if (conn == -1) {
+ free(client_addr);
+ return std::shared_ptr<Socket>();
+ }
+
+ std::string client_uri = _uri;
+ if (_type != Type::UNIX) {
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ if (!getnameinfo(client_addr, client_addr_len,
+ host, sizeof(host), serv, sizeof(serv), 0)) {
+ const std::string scheme = _uri.substr(0, _uri.find(':'));
+ client_uri = scheme + "://" + host + ":" + serv;
+ }
+ }
+
+ return std::make_shared<Socket>(
+ _type, client_uri, client_addr, client_addr_len, conn);
+}
+
+inline void
+Socket::close()
+{
+ if (_sock != -1) {
+ ::close(_sock);
+ _sock = -1;
+ }
+}
+
+inline void
+Socket::shutdown()
+{
+ if (_sock != -1) {
+ ::shutdown(_sock, SHUT_RDWR);
+ }
+}
+
+} // namespace Raul
+
+#endif // RAUL_SOCKET_HPP
diff --git a/include/raul/Symbol.hpp b/include/raul/Symbol.hpp
new file mode 100644
index 0000000..70062be
--- /dev/null
+++ b/include/raul/Symbol.hpp
@@ -0,0 +1,130 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2014 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/>.
+*/
+
+#ifndef RAUL_SYMBOL_HPP
+#define RAUL_SYMBOL_HPP
+
+#include "raul/Exception.hpp"
+
+#include <string>
+
+namespace Raul {
+
+/** 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 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
+ */
+class Symbol : public std::basic_string<char> {
+public:
+ /** Attempt to construct an invalid Symbol. */
+ class BadSymbol : public Exception {
+ public:
+ explicit BadSymbol(const std::string& symbol) : Exception(symbol) {}
+ };
+
+ /** Construct a Symbol from a C++ string.
+ *
+ * This will throw an exception if `symbol` is invalid. To avoid this,
+ * use is_valid() first to check.
+ */
+ explicit Symbol(const std::basic_string<char>& symbol)
+ : std::basic_string<char>(symbol)
+ {
+ if (!is_valid(symbol)) {
+ throw BadSymbol(symbol);
+ }
+ }
+
+ /** Construct a Symbol from a C string.
+ *
+ * This will throw an exception if `symbol` is invalid. To avoid this,
+ * use is_valid() first to check.
+ */
+ explicit Symbol(const char* symbol)
+ : std::basic_string<char>(symbol)
+ {
+ if (!is_valid(symbol)) {
+ throw BadSymbol(symbol);
+ }
+ }
+
+ /** Copy a Symbol.
+ *
+ * Note this is faster than constructing a Symbol from another Symbol's
+ * string since validation is unnecessary.
+ */
+ Symbol(const Symbol& symbol) = default;
+
+ /** Return true iff `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');
+ }
+
+ /** Return true iff `c` is a valid Symbol character. */
+ static inline bool is_valid_char(char c) {
+ return is_valid_start_char(c) || (c >= '0' && c <= '9');
+ }
+
+ /** Return true iff `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
+ }
+
+ for (auto c : str) {
+ if (!is_valid_char(c)) {
+ return false; // All characters must be _, a-z, A-Z, 0-9
+ }
+ }
+
+ return true;
+ }
+
+ /** Convert a string to a valid symbol.
+ *
+ * This will make a best effort at turning `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("_");
+ }
+
+ std::basic_string<char> out(in);
+ for (size_t i = 0; i < in.length(); ++i) {
+ if (!is_valid_char(out[i])) {
+ out[i] = '_';
+ }
+ }
+
+ if (is_valid_start_char(out[0])) {
+ return Symbol(out);
+ } else {
+ return Symbol(std::string("_") + out);
+ }
+ }
+};
+
+} // namespace Raul
+
+#endif // RAUL_SYMBOL_HPP
diff --git a/include/raul/TimeSlice.hpp b/include/raul/TimeSlice.hpp
new file mode 100644
index 0000000..7758602
--- /dev/null
+++ b/include/raul/TimeSlice.hpp
@@ -0,0 +1,157 @@
+/*
+ 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/>.
+*/
+
+#ifndef RAUL_TIMESLICE_HPP
+#define RAUL_TIMESLICE_HPP
+
+#include "raul/Noncopyable.hpp"
+#include "raul/TimeStamp.hpp"
+
+#include <cassert>
+#include <cmath>
+
+namespace Raul {
+
+/* FIXME: all the conversion here is wrong now */
+
+/** A duration of time, with conversion between tick time and beat time.
+ *
+ * This is a slice along a single timeline (ie t=0 in ticks and t=0 in beats
+ * are equal). Relation to an external time base (e.g. Jack frame time) is
+ * represented by frame_offset (the idea is that this holds all the information
+ * necessary for passing to run() methods so they know the current state of
+ * things WRT time).
+ *
+ * This class handles conversion between two units of time: musical
+ * (beat) time, and real (tick) time. Real time is discrete, the smallest
+ * unit of time is the 'tick' (usually audio frames or MIDI ticks). Beat time
+ * is stored as a double (to be independent of any rates or timer precision).
+ *
+ * This caches as many values as possible to make calls less expensive, pass it
+ * around by reference, not value.
+ *
+ * \ingroup raul
+ */
+class TimeSlice : public Noncopyable {
+public:
+ TimeSlice(uint32_t rate, uint32_t ppqn, double bpm)
+ : _tick_rate(rate)
+ , _beat_rate(60.0/bpm)
+ , _start_ticks(Raul::TimeUnit(Raul::TimeUnit::FRAMES, rate), 0, 0)
+ , _length_ticks(TimeUnit(TimeUnit::FRAMES, rate), 0, 0)
+ , _start_beats(TimeUnit(TimeUnit::BEATS, ppqn), 0, 0)
+ , _length_beats(TimeUnit(TimeUnit::BEATS, ppqn), 0, 0)
+ , _offset_ticks(TimeUnit(TimeUnit::FRAMES, rate), 0, 0)
+ {}
+
+ /** Set the start and length of the slice.
+ *
+ * Note that external offset is not affected by this, don't forget to reset
+ * the offset each cycle!
+ */
+ void set_slice(TimeStamp start, TimeDuration length) {
+ assert(start.unit() == ticks_unit());
+ assert(length.unit() == ticks_unit());
+ _start_ticks = start;
+ _length_ticks = length;
+ update_beat_time();
+ }
+
+ void set_length(TimeDuration length) {
+ assert(length.unit() == ticks_unit());
+ _length_ticks = length;
+ _length_beats = ticks_to_beats(_length_ticks);
+ }
+
+ bool contains(TimeStamp time) const {
+ return (time >= start_ticks() && time < start_ticks() + length_ticks());
+ }
+
+ double tick_rate() const { return _tick_rate; }
+ double beat_rate() const { return _beat_rate; }
+ double bpm() const { return 60/_beat_rate; }
+
+ void set_tick_rate(double tick_rate) {
+ _tick_rate = tick_rate;
+ update_beat_time();
+ }
+
+ void set_bpm(double bpm) {
+ _beat_rate = 60.0/bpm;
+ update_beat_time();
+ }
+
+ inline TimeStamp beats_to_seconds(TimeStamp beats) const {
+ return TimeStamp(real_unit(), beats.to_double() * 1/_beat_rate);
+ }
+
+ inline TimeStamp beats_to_ticks(TimeStamp beats) const {
+ return TimeStamp(ticks_unit(), beats.to_double() * _beat_rate * _tick_rate);
+ }
+
+ inline TimeStamp ticks_to_seconds(TimeStamp ticks) const {
+ return {real_unit(), ticks.ticks() * 1/_tick_rate};
+ }
+
+ inline TimeStamp ticks_to_beats(TimeStamp ticks) const {
+ return {beats_unit(), ticks.ticks() * 1/_tick_rate * _beat_rate};
+ }
+
+ /** Start of current sub-cycle in ticks */
+ inline TimeStamp start_ticks() const { return _start_ticks; }
+
+ /** Length of current sub-cycle in ticks */
+ inline TimeDuration length_ticks() const { return _length_ticks; }
+
+ /** Start of current sub-cycle in beats */
+ inline TimeStamp start_beats() const { return _start_beats; }
+
+ /** Length of current sub-cycle in beats */
+ inline TimeDuration length_beats() const { return _length_beats; }
+
+ /** Set the offset between real-time and timeslice-time. */
+ inline void set_offset(TimeDuration offset) { _offset_ticks = offset; }
+
+ /** Offset relative to external (e.g Jack) time */
+ inline TimeDuration offset_ticks() const { return _offset_ticks; }
+
+ inline TimeUnit beats_unit() const { return _start_beats.unit(); }
+ inline TimeUnit ticks_unit() const { return _start_ticks.unit(); }
+
+ static inline TimeUnit real_unit() { return {TimeUnit::SECONDS, 0}; }
+
+private:
+ inline void update_beat_time() {
+ _start_beats = ticks_to_beats(_start_ticks);
+ _length_beats = ticks_to_beats(_length_ticks);
+ }
+
+ // Rate/Tempo
+ double _tick_rate; ///< Tick rate in Hz (e.g. sample rate)
+ double _beat_rate; ///< Beat rate in Hz
+
+ // Current time
+ TimeStamp _start_ticks; ///< Current window start in ticks
+ TimeDuration _length_ticks; ///< Current window length in ticks
+ TimeStamp _start_beats; ///< Current window start in beats
+ TimeDuration _length_beats; ///< Current window length in beats
+
+ TimeDuration _offset_ticks; ///< Offset to global time
+};
+
+} // namespace Raul
+
+#endif // RAUL_TIMESLICE_HPP
diff --git a/include/raul/TimeStamp.hpp b/include/raul/TimeStamp.hpp
new file mode 100644
index 0000000..d8d4939
--- /dev/null
+++ b/include/raul/TimeStamp.hpp
@@ -0,0 +1,236 @@
+/*
+ This file is part of Raul.
+ Copyright 2007-2014 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/>.
+*/
+
+#ifndef RAUL_TIMESTAMP_HPP
+#define RAUL_TIMESTAMP_HPP
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <iostream>
+#include <limits>
+
+namespace Raul {
+
+/** A type of time stamp
+ * \ingroup raul
+ */
+class TimeUnit {
+public:
+ enum Type {
+ FRAMES,
+ BEATS,
+ SECONDS
+ };
+
+ /** `ppt` (parts per tick) is the sample rate for FRAMES, PPQN for BEATS,
+ * and ignored for SECONDS.
+ */
+ inline TimeUnit(Type type, uint32_t ppt)
+ : _type(type)
+ , _ppt(ppt)
+ {
+ assert(type == SECONDS || ppt != 0);
+ _type = type;
+ _ppt = ppt;
+ }
+
+ static inline TimeUnit frames(uint32_t srate) { return {FRAMES, srate}; }
+ static inline TimeUnit beats(uint32_t ppqn) { return {BEATS, ppqn}; }
+ static inline TimeUnit seconds() { return {BEATS, std::numeric_limits<uint32_t>::max()}; }
+
+ inline Type type() const { return _type; }
+ inline uint32_t ppt() const { return _ppt; }
+
+ inline bool operator==(const TimeUnit& rhs) const {
+ return (_type == rhs._type && _ppt == rhs._ppt);
+ }
+
+ inline bool operator!=(const TimeUnit& rhs) const {
+ return (_type != rhs._type || _ppt != rhs._ppt);
+ }
+
+private:
+ Type _type;
+ uint32_t _ppt;
+};
+
+/** A real-time time stamp (possible units: frame, absolute (s), or beat).
+ *
+ * This is a uint32_t:uint32_t fixed point representation, capable of
+ * sub-sample accurate frame time, beat time (at any remotely sane
+ * tempo and sample rate), and absolute time. The absolute time (seconds)
+ * is compatible with standard OSC/NTP time stamps.
+ *
+ * \ingroup raul
+ */
+class TimeStamp {
+public:
+ explicit TimeStamp(TimeUnit unit, uint32_t ticks = 0, uint32_t subticks = 0)
+ : _ticks(ticks)
+ , _subticks(subticks)
+ , _unit(unit)
+ {}
+
+ inline TimeStamp(TimeUnit unit, double dec)
+ : _ticks(0u)
+ , _subticks(0u)
+ , _unit(unit)
+ {
+ dec = std::max(0.0, dec);
+ dec = std::min(double(std::numeric_limits<uint32_t>::max()), dec);
+ double integral = 0.0;
+ const double fractional = modf(dec, &integral);
+ _ticks = static_cast<uint32_t>(integral);
+ _subticks = static_cast<uint32_t>(fractional * unit.ppt());
+ }
+
+ inline TimeStamp(const TimeStamp&) = default;
+ TimeStamp& operator=(const TimeStamp&) = default;
+
+ inline TimeUnit unit() const { return _unit; }
+ inline uint32_t ticks() const { return _ticks; }
+ inline uint32_t subticks() const { return _subticks; }
+
+ inline double to_double() const {
+ return _ticks + (_subticks / static_cast<double>(_unit.ppt()));
+ }
+
+ inline bool is_zero() const {
+ return _ticks == 0 && _subticks == 0;
+ }
+
+ inline TimeStamp& operator=(uint32_t ticks) {
+ _ticks = ticks;
+ _subticks = 0;
+ return *this;
+ }
+
+ inline bool operator==(const TimeStamp& rhs) const {
+ return _ticks == rhs._ticks
+ && _subticks == rhs._subticks
+ && _unit == rhs._unit;
+ }
+
+ inline bool operator!=(const TimeStamp& rhs) const {
+ return !operator==(rhs);
+ }
+
+ inline bool operator<(const TimeStamp& rhs) const {
+ assert(_unit == rhs._unit);
+ return (_ticks < rhs._ticks
+ || (_ticks == rhs._ticks && _subticks < rhs._subticks));
+ }
+
+ inline bool operator>(const TimeStamp& rhs) const {
+ assert(_unit == rhs._unit);
+ return (_ticks > rhs._ticks
+ || (_ticks == rhs._ticks && _subticks > rhs._subticks));
+ }
+
+ inline bool operator<=(const TimeStamp& rhs) const {
+ return (*this == rhs) || ((*this) < rhs);
+ }
+
+ inline bool operator>=(const TimeStamp& rhs) const {
+ return (*this == rhs) || ((*this) > rhs);
+ }
+
+ inline TimeStamp& operator+=(const TimeStamp& rhs) {
+ assert(_unit == rhs._unit);
+ _ticks += rhs._ticks;
+ if (_subticks + rhs._subticks <= _unit.ppt()) {
+ _subticks += rhs._subticks;
+ } else if (rhs._subticks > 0) {
+ ++_ticks;
+ _subticks = rhs._subticks + _subticks - _unit.ppt();
+ }
+ return *this;
+ }
+
+ inline TimeStamp& operator-=(const TimeStamp& rhs) {
+ assert(_unit == rhs._unit);
+ assert(rhs <= *this);
+ _ticks -= rhs._ticks;
+ if (_subticks >= rhs._subticks) {
+ _subticks -= rhs._subticks;
+ } else if (rhs._subticks > 0) {
+ --_ticks;
+ _subticks = _unit.ppt() - (rhs._subticks - _subticks);
+ }
+ return *this;
+ }
+
+ inline TimeStamp operator+(const TimeStamp& rhs) const {
+ assert(_unit == rhs._unit);
+ TimeStamp result = *this;
+ result += rhs;
+ return result;
+ }
+
+ inline TimeStamp operator-(const TimeStamp& rhs) const {
+ assert(_unit == rhs._unit);
+ TimeStamp result = *this;
+ result -= rhs;
+ return result;
+ }
+
+private:
+ uint32_t _ticks;
+ uint32_t _subticks;
+ TimeUnit _unit;
+};
+
+static inline std::ostream&
+operator<<(std::ostream& os, const TimeStamp& t)
+{
+ os << t.ticks() << ":" << t.subticks();
+ switch (t.unit().type()) {
+ case TimeUnit::FRAMES:
+ os << " frames";
+ break;
+ case TimeUnit::BEATS:
+ os << " beats";
+ break;
+ case TimeUnit::SECONDS:
+ os << " seconds";
+ break;
+ }
+ return os;
+}
+
+class FrameStamp : public TimeStamp {
+public:
+ explicit FrameStamp(uint32_t rate, uint32_t ticks = 0, uint32_t subticks = 0)
+ : TimeStamp(TimeUnit(TimeUnit::FRAMES, rate), ticks, subticks)
+ {}
+};
+
+class BeatStamp : public TimeStamp {
+public:
+ explicit BeatStamp(uint32_t ppqn, uint32_t ticks = 0, uint32_t subticks = 0)
+ : TimeStamp(TimeUnit(TimeUnit::BEATS, ppqn), ticks, subticks)
+ {}
+};
+
+/** Same thing as TimeStamp really, but makes code clearer and enforces
+ * correct semantics via type safety */
+using TimeDuration = TimeStamp;
+
+} // namespace Raul
+
+#endif // RAUL_TIMESTAMP_HPP