From bf9190ef628c1aa04791af1bd7cd4905e9c24658 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 12 Nov 2020 01:11:11 +0100 Subject: Move includes to a conventional include directory --- include/raul/Array.hpp | 160 +++++++++++++++++++++++++ include/raul/Deletable.hpp | 37 ++++++ include/raul/DoubleBuffer.hpp | 94 +++++++++++++++ include/raul/Exception.hpp | 40 +++++++ include/raul/Maid.hpp | 148 +++++++++++++++++++++++ include/raul/Noncopyable.hpp | 34 ++++++ include/raul/Path.hpp | 207 ++++++++++++++++++++++++++++++++ include/raul/Process.hpp | 87 ++++++++++++++ include/raul/RingBuffer.hpp | 219 ++++++++++++++++++++++++++++++++++ include/raul/Semaphore.hpp | 270 ++++++++++++++++++++++++++++++++++++++++++ include/raul/Socket.hpp | 262 ++++++++++++++++++++++++++++++++++++++++ include/raul/Symbol.hpp | 130 ++++++++++++++++++++ include/raul/TimeSlice.hpp | 157 ++++++++++++++++++++++++ include/raul/TimeStamp.hpp | 236 ++++++++++++++++++++++++++++++++++++ raul/Array.hpp | 160 ------------------------- raul/Deletable.hpp | 37 ------ raul/DoubleBuffer.hpp | 94 --------------- raul/Exception.hpp | 40 ------- raul/Maid.hpp | 148 ----------------------- raul/Noncopyable.hpp | 34 ------ raul/Path.hpp | 207 -------------------------------- raul/Process.hpp | 87 -------------- raul/RingBuffer.hpp | 219 ---------------------------------- raul/Semaphore.hpp | 270 ------------------------------------------ raul/Socket.hpp | 262 ---------------------------------------- raul/Symbol.hpp | 130 -------------------- raul/TimeSlice.hpp | 157 ------------------------ raul/TimeStamp.hpp | 236 ------------------------------------ waflib | 2 +- wscript | 7 +- 30 files changed, 2087 insertions(+), 2084 deletions(-) create mode 100644 include/raul/Array.hpp create mode 100644 include/raul/Deletable.hpp create mode 100644 include/raul/DoubleBuffer.hpp create mode 100644 include/raul/Exception.hpp create mode 100644 include/raul/Maid.hpp create mode 100644 include/raul/Noncopyable.hpp create mode 100644 include/raul/Path.hpp create mode 100644 include/raul/Process.hpp create mode 100644 include/raul/RingBuffer.hpp create mode 100644 include/raul/Semaphore.hpp create mode 100644 include/raul/Socket.hpp create mode 100644 include/raul/Symbol.hpp create mode 100644 include/raul/TimeSlice.hpp create mode 100644 include/raul/TimeStamp.hpp delete mode 100644 raul/Array.hpp delete mode 100644 raul/Deletable.hpp delete mode 100644 raul/DoubleBuffer.hpp delete mode 100644 raul/Exception.hpp delete mode 100644 raul/Maid.hpp delete mode 100644 raul/Noncopyable.hpp delete mode 100644 raul/Path.hpp delete mode 100644 raul/Process.hpp delete mode 100644 raul/RingBuffer.hpp delete mode 100644 raul/Semaphore.hpp delete mode 100644 raul/Socket.hpp delete mode 100644 raul/Symbol.hpp delete mode 100644 raul/TimeSlice.hpp delete mode 100644 raul/TimeStamp.hpp 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 + + 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 . +*/ + +#ifndef RAUL_ARRAY_HPP +#define RAUL_ARRAY_HPP + +#include "raul/Maid.hpp" + +#include +#include +#include +#include + +namespace Raul { + +/** A disposable array with size. + * + * \ingroup raul + */ +template +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& 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& operator=(const Array& 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&& array) noexcept + : _size(array._size) + , _elems(std::move(array._elems)) + { + } + + Array& operator=(Array&& array) noexcept + { + _size = array._size; + _elems = std::move(array._elems); + return *this; + } + + Array(size_t size, const Array& 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& 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(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(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 _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 + + 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 . +*/ + +#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 + + 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 . +*/ + +#ifndef RAUL_DOUBLEBUFFER_HPP +#define RAUL_DOUBLEBUFFER_HPP + +#include +#include + +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 +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; + 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 + + 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 . +*/ + +#include +#include +#include + +#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 + + 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 . +*/ + +#ifndef RAUL_MAID_HPP +#define RAUL_MAID_HPP + +#include "raul/Deletable.hpp" +#include "raul/Noncopyable.hpp" + +#include +#include + +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 + class Managed : public Raul::Maid::Disposable, public T + { + public: + template + explicit Managed(Args&&... args) + : T(std::forward(args)...) + {} + }; + + /** Deleter for Disposable objects. */ + template + 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 using managed_ptr = std::unique_ptr>; + + 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 + managed_ptr make_managed(Args&&... args) { + return std::unique_ptr >( + new T(std::forward(args)...), + Disposer(this)); + } + +private: + std::atomic _disposed; +}; + +template +using managed_ptr = Maid::managed_ptr; + +} // 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 + + 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 . +*/ + +#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 + + 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 . +*/ + +#ifndef RAUL_PATH_HPP +#define RAUL_PATH_HPP + +#include "raul/Exception.hpp" +#include "raul/Symbol.hpp" + +#include +#include + +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 { +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("/") {} + + /** 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& path) + : std::basic_string(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(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& 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 + + 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 . +*/ + +#ifndef RAUL_PROCESS_HPP +#define RAUL_PROCESS_HPP + +#include "raul/Noncopyable.hpp" + +#include +#include +#include + +#include + +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(fd)); + } + + // Fork child + const int grandchild = fork(); + switch (grandchild) { + case 0: + // (in grandchild) + setsid(); + execvp(argv[0], const_cast(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 + + 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 . +*/ + +#ifndef RAUL_RINGBUFFER_HPP +#define RAUL_RINGBUFFER_HPP + +#include "raul/Noncopyable.hpp" + +#include +#include +#include +#include +#include +#include + +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(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(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 _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 + + 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 . +*/ + +#ifndef RAUL_SEMAPHORE_HPP +#define RAUL_SEMAPHORE_HPP + +#ifdef __APPLE__ +# include +#elif defined(_WIN32) +# define NOMINMAX +# include +#else +# include +# include +# include +#endif + +#include +#include + +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 + inline bool timed_wait(const std::chrono::duration& 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 +inline bool +Semaphore::timed_wait(const std::chrono::duration& wait) +{ + namespace chr = std::chrono; + + const chr::seconds sec(chr::duration_cast(wait)); + const chr::nanoseconds nsec(wait - sec); + + const mach_timespec_t t = { static_cast(sec.count()), + static_cast(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 +inline bool +Semaphore::timed_wait(const std::chrono::duration& wait) +{ + namespace chr = std::chrono; + + const chr::milliseconds ms(chr::duration_cast(wait)); + return WaitForSingleObject( + _sem, static_cast(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 +inline bool +Semaphore::timed_wait(const std::chrono::duration& 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(end)); + const chr::nanoseconds end_nsec(end - end_sec); + + const struct timespec ts_end = { static_cast(end_sec.count()), + static_cast(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 + + 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 . +*/ + +#ifndef RAUL_SOCKET_HPP +#define RAUL_SOCKET_HPP + +#include "raul/Noncopyable.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 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( + 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(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(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::accept() +{ + socklen_t client_addr_len = _addr_len; + auto* const client_addr = + static_cast(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(); + } + + 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( + _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 + + 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 . +*/ + +#ifndef RAUL_SYMBOL_HPP +#define RAUL_SYMBOL_HPP + +#include "raul/Exception.hpp" + +#include + +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 { +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& symbol) + : std::basic_string(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(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& 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& in) { + if (in.empty()) { + return Symbol("_"); + } + + std::basic_string 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 + + 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 . +*/ + +#ifndef RAUL_TIMESLICE_HPP +#define RAUL_TIMESLICE_HPP + +#include "raul/Noncopyable.hpp" +#include "raul/TimeStamp.hpp" + +#include +#include + +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 + + 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 . +*/ + +#ifndef RAUL_TIMESTAMP_HPP +#define RAUL_TIMESTAMP_HPP + +#include +#include +#include +#include +#include +#include + +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::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::max()), dec); + double integral = 0.0; + const double fractional = modf(dec, &integral); + _ticks = static_cast(integral); + _subticks = static_cast(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(_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 diff --git a/raul/Array.hpp b/raul/Array.hpp deleted file mode 100644 index ee50470..0000000 --- a/raul/Array.hpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2013 David Robillard - - 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 . -*/ - -#ifndef RAUL_ARRAY_HPP -#define RAUL_ARRAY_HPP - -#include "raul/Maid.hpp" - -#include -#include -#include -#include - -namespace Raul { - -/** A disposable array with size. - * - * \ingroup raul - */ -template -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& 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& operator=(const Array& 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&& array) noexcept - : _size(array._size) - , _elems(std::move(array._elems)) - { - } - - Array& operator=(Array&& array) noexcept - { - _size = array._size; - _elems = std::move(array._elems); - return *this; - } - - Array(size_t size, const Array& 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& 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(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(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 _elems; -}; - -} // namespace Raul - -#endif // RAUL_ARRAY_HPP diff --git a/raul/Deletable.hpp b/raul/Deletable.hpp deleted file mode 100644 index 9b80d54..0000000 --- a/raul/Deletable.hpp +++ /dev/null @@ -1,37 +0,0 @@ - -/* - This file is part of Raul. - Copyright 2007-2013 David Robillard - - 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 . -*/ - -#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/raul/DoubleBuffer.hpp b/raul/DoubleBuffer.hpp deleted file mode 100644 index 73b5565..0000000 --- a/raul/DoubleBuffer.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2013 David Robillard - - 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 . -*/ - -#ifndef RAUL_DOUBLEBUFFER_HPP -#define RAUL_DOUBLEBUFFER_HPP - -#include -#include - -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 -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; - T _vals[2]; -}; - -} // namespace Raul - -#endif // RAUL_DOUBLEBUFFER_HPP diff --git a/raul/Exception.hpp b/raul/Exception.hpp deleted file mode 100644 index da4a0e0..0000000 --- a/raul/Exception.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard - - 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 . -*/ - -#include -#include -#include - -#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/raul/Maid.hpp b/raul/Maid.hpp deleted file mode 100644 index 1479f71..0000000 --- a/raul/Maid.hpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2017 David Robillard - - 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 . -*/ - -#ifndef RAUL_MAID_HPP -#define RAUL_MAID_HPP - -#include "raul/Deletable.hpp" -#include "raul/Noncopyable.hpp" - -#include -#include - -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 - class Managed : public Raul::Maid::Disposable, public T - { - public: - template - explicit Managed(Args&&... args) - : T(std::forward(args)...) - {} - }; - - /** Deleter for Disposable objects. */ - template - 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 using managed_ptr = std::unique_ptr>; - - 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 - managed_ptr make_managed(Args&&... args) { - return std::unique_ptr >( - new T(std::forward(args)...), - Disposer(this)); - } - -private: - std::atomic _disposed; -}; - -template -using managed_ptr = Maid::managed_ptr; - -} // namespace Raul - -#endif // RAUL_MAID_HPP diff --git a/raul/Noncopyable.hpp b/raul/Noncopyable.hpp deleted file mode 100644 index 876cee8..0000000 --- a/raul/Noncopyable.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2013 David Robillard - - 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 . -*/ - -#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/raul/Path.hpp b/raul/Path.hpp deleted file mode 100644 index 6237ddd..0000000 --- a/raul/Path.hpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2014 David Robillard - - 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 . -*/ - -#ifndef RAUL_PATH_HPP -#define RAUL_PATH_HPP - -#include "raul/Exception.hpp" -#include "raul/Symbol.hpp" - -#include -#include - -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 { -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("/") {} - - /** 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& path) - : std::basic_string(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(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& 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/raul/Process.hpp b/raul/Process.hpp deleted file mode 100644 index 717a900..0000000 --- a/raul/Process.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2015 David Robillard - - 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 . -*/ - -#ifndef RAUL_PROCESS_HPP -#define RAUL_PROCESS_HPP - -#include "raul/Noncopyable.hpp" - -#include -#include -#include - -#include - -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(fd)); - } - - // Fork child - const int grandchild = fork(); - switch (grandchild) { - case 0: - // (in grandchild) - setsid(); - execvp(argv[0], const_cast(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/raul/RingBuffer.hpp b/raul/RingBuffer.hpp deleted file mode 100644 index a7bbfb7..0000000 --- a/raul/RingBuffer.hpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard - - 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 . -*/ - -#ifndef RAUL_RINGBUFFER_HPP -#define RAUL_RINGBUFFER_HPP - -#include "raul/Noncopyable.hpp" - -#include -#include -#include -#include -#include -#include - -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(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(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 _buf; ///< Contents -}; - -} // namespace Raul - -#endif // RAUL_RINGBUFFER_HPP diff --git a/raul/Semaphore.hpp b/raul/Semaphore.hpp deleted file mode 100644 index ee2325f..0000000 --- a/raul/Semaphore.hpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2014 David Robillard - - 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 . -*/ - -#ifndef RAUL_SEMAPHORE_HPP -#define RAUL_SEMAPHORE_HPP - -#ifdef __APPLE__ -# include -#elif defined(_WIN32) -# define NOMINMAX -# include -#else -# include -# include -# include -#endif - -#include -#include - -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 - inline bool timed_wait(const std::chrono::duration& 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 -inline bool -Semaphore::timed_wait(const std::chrono::duration& wait) -{ - namespace chr = std::chrono; - - const chr::seconds sec(chr::duration_cast(wait)); - const chr::nanoseconds nsec(wait - sec); - - const mach_timespec_t t = { static_cast(sec.count()), - static_cast(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 -inline bool -Semaphore::timed_wait(const std::chrono::duration& wait) -{ - namespace chr = std::chrono; - - const chr::milliseconds ms(chr::duration_cast(wait)); - return WaitForSingleObject( - _sem, static_cast(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 -inline bool -Semaphore::timed_wait(const std::chrono::duration& 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(end)); - const chr::nanoseconds end_nsec(end - end_sec); - - const struct timespec ts_end = { static_cast(end_sec.count()), - static_cast(end_nsec.count()) }; - - return (sem_timedwait(&_sem, &ts_end) == 0); -} - -#endif - -} // namespace Raul - -#endif // RAUL_SEMAPHORE_HPP diff --git a/raul/Socket.hpp b/raul/Socket.hpp deleted file mode 100644 index 579a5cd..0000000 --- a/raul/Socket.hpp +++ /dev/null @@ -1,262 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2015 David Robillard - - 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 . -*/ - -#ifndef RAUL_SOCKET_HPP -#define RAUL_SOCKET_HPP - -#include "raul/Noncopyable.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -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 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( - 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(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(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::accept() -{ - socklen_t client_addr_len = _addr_len; - auto* const client_addr = - static_cast(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(); - } - - 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( - _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/raul/Symbol.hpp b/raul/Symbol.hpp deleted file mode 100644 index 70062be..0000000 --- a/raul/Symbol.hpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2014 David Robillard - - 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 . -*/ - -#ifndef RAUL_SYMBOL_HPP -#define RAUL_SYMBOL_HPP - -#include "raul/Exception.hpp" - -#include - -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 { -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& symbol) - : std::basic_string(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(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& 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& in) { - if (in.empty()) { - return Symbol("_"); - } - - std::basic_string 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/raul/TimeSlice.hpp b/raul/TimeSlice.hpp deleted file mode 100644 index 7758602..0000000 --- a/raul/TimeSlice.hpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2012 David Robillard - - 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 . -*/ - -#ifndef RAUL_TIMESLICE_HPP -#define RAUL_TIMESLICE_HPP - -#include "raul/Noncopyable.hpp" -#include "raul/TimeStamp.hpp" - -#include -#include - -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/raul/TimeStamp.hpp b/raul/TimeStamp.hpp deleted file mode 100644 index d8d4939..0000000 --- a/raul/TimeStamp.hpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - This file is part of Raul. - Copyright 2007-2014 David Robillard - - 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 . -*/ - -#ifndef RAUL_TIMESTAMP_HPP -#define RAUL_TIMESTAMP_HPP - -#include -#include -#include -#include -#include -#include - -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::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::max()), dec); - double integral = 0.0; - const double fractional = modf(dec, &integral); - _ticks = static_cast(integral); - _subticks = static_cast(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(_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 diff --git a/waflib b/waflib index 116a28f..d199922 160000 --- a/waflib +++ b/waflib @@ -1 +1 @@ -Subproject commit 116a28fa5f92e58a46dc878751019ba74a61e54a +Subproject commit d19992202543ebb810609c074f754b5ec48c0fd7 diff --git a/wscript b/wscript index 7662719..ebddb46 100644 --- a/wscript +++ b/wscript @@ -63,7 +63,10 @@ def configure(conf): conf.check_cxx(header_name='memory') conf.check_cxx(header_name='atomic') - autowaf.set_lib_env(conf, 'raul', RAUL_VERSION, has_objects=False) + autowaf.set_lib_env(conf, 'raul', RAUL_VERSION, + has_objects=False, + include_path=str(conf.path.find_node('include'))) + conf.write_config_header('raul_config.h', remove=False) autowaf.display_summary(conf, @@ -119,7 +122,7 @@ def build(bld): for i in tests: bld(features = 'cxx cxxprogram', source = os.path.join('test', i + '.cpp'), - includes = ['.'], + includes = ['include'], lib = test_libs, use = 'libraul_static', uselib = 'GLIB GTHREAD', -- cgit v1.2.1