diff options
author | David Robillard <d@drobilla.net> | 2021-01-02 14:46:29 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2021-01-02 14:46:29 +0100 |
commit | bd0214b1da66225f410641692e89e492f668472a (patch) | |
tree | 80956f373070734dc14a7f103e5fa21a5baa0b6e /include | |
parent | 5c263125aeae87dcd4694a4d3a4781bda7247a00 (diff) | |
download | raul-bd0214b1da66225f410641692e89e492f668472a.tar.gz raul-bd0214b1da66225f410641692e89e492f668472a.tar.bz2 raul-bd0214b1da66225f410641692e89e492f668472a.zip |
Format all code with clang-format
Diffstat (limited to 'include')
-rw-r--r-- | include/raul/Array.hpp | 238 | ||||
-rw-r--r-- | include/raul/Deletable.hpp | 12 | ||||
-rw-r--r-- | include/raul/DoubleBuffer.hpp | 120 | ||||
-rw-r--r-- | include/raul/Exception.hpp | 13 | ||||
-rw-r--r-- | include/raul/Maid.hpp | 235 | ||||
-rw-r--r-- | include/raul/Noncopyable.hpp | 15 | ||||
-rw-r--r-- | include/raul/Path.hpp | 359 | ||||
-rw-r--r-- | include/raul/Process.hpp | 89 | ||||
-rw-r--r-- | include/raul/RingBuffer.hpp | 367 | ||||
-rw-r--r-- | include/raul/Semaphore.hpp | 204 | ||||
-rw-r--r-- | include/raul/Socket.hpp | 337 | ||||
-rw-r--r-- | include/raul/Symbol.hpp | 186 | ||||
-rw-r--r-- | include/raul/TimeSlice.hpp | 215 | ||||
-rw-r--r-- | include/raul/TimeStamp.hpp | 360 |
14 files changed, 1420 insertions, 1330 deletions
diff --git a/include/raul/Array.hpp b/include/raul/Array.hpp index a80038f..9e7e24c 100644 --- a/include/raul/Array.hpp +++ b/include/raul/Array.hpp @@ -29,129 +29,131 @@ namespace Raul { * * \ingroup raul */ -template <class T> +template<class T> class Array : public Maid::Disposable { public: - explicit Array(size_t size = 0) - : Maid::Disposable() - , _size(size) - , _elems(size ? new T[size] : nullptr) - { - } - - Array(size_t size, T initial_value) - : Maid::Disposable() - , _size(size) - , _elems(size ? new T[size] : nullptr) - { - if (size > 0) { - for (size_t i = 0; i < size; ++i) { - _elems[i] = initial_value; - } - } - } - - Array(const Array<T>& array) - : Maid::Disposable() - , _size(array._size) - , _elems(_size ? new T[_size] : nullptr) - { - for (size_t i = 0; i < _size; ++i) { - _elems[i] = array._elems[i]; - } - } - - ~Array() override = default; - - Array<T>& operator=(const Array<T>& array) - { - if (&array == this) { - return *this; - } - - _size = array._size; - _elems = _size ? new T[_size] : nullptr; - - for (size_t i = 0; i < _size; ++i) { - _elems[i] = array._elems[i]; - } - } - - Array(Array<T>&& array) noexcept - : _size(array._size) - , _elems(std::move(array._elems)) - { - } - - Array<T>& operator=(Array<T>&& array) noexcept - { - _size = array._size; - _elems = std::move(array._elems); - return *this; - } - - Array(size_t size, const Array<T>& contents) - : _size(size) - , _elems(size ? new T[size] : nullptr) - { - assert(contents.size() >= size); - for (size_t i = 0; i < std::min(size, contents.size()); ++i) { - _elems[i] = contents[i]; - } - } - - Array(size_t size, const Array<T>& contents, T initial_value = T()) - : _size(size) - , _elems(size ? new T[size] : nullptr) - { - const size_t end = std::min(size, contents.size()); - for (size_t i = 0; i < end; ++i) { - _elems[i] = contents[i]; - } - for (size_t i = end; i < size; ++i) { - _elems[i] = initial_value; - } - } - - virtual void alloc(size_t num_elems) { - _size = num_elems; - - if (num_elems > 0) { - _elems = std::unique_ptr<T[]>(new T[num_elems]); - } else { - _elems.reset(); - } - } - - virtual void alloc(size_t num_elems, T initial_value) { - _size = num_elems; - - if (num_elems > 0) { - _elems = std::unique_ptr<T[]>(new T[num_elems]); - for (size_t i = 0; i < _size; ++i) { - _elems[i] = initial_value; - } - } else { - _elems.reset(); - } - } - - inline size_t size() const { return _size; } - - inline T& operator[](size_t i) const { - assert(i < _size); - return _elems[i]; - } - - inline T& at(size_t i) const { - assert(i < _size); - return _elems[i]; - } + explicit Array(size_t size = 0) + : Maid::Disposable() + , _size(size) + , _elems(size ? new T[size] : nullptr) + {} + + Array(size_t size, T initial_value) + : Maid::Disposable() + , _size(size) + , _elems(size ? new T[size] : nullptr) + { + if (size > 0) { + for (size_t i = 0; i < size; ++i) { + _elems[i] = initial_value; + } + } + } + + Array(const Array<T>& array) + : Maid::Disposable() + , _size(array._size) + , _elems(_size ? new T[_size] : nullptr) + { + for (size_t i = 0; i < _size; ++i) { + _elems[i] = array._elems[i]; + } + } + + ~Array() override = default; + + Array<T>& operator=(const Array<T>& array) + { + if (&array == this) { + return *this; + } + + _size = array._size; + _elems = _size ? new T[_size] : nullptr; + + for (size_t i = 0; i < _size; ++i) { + _elems[i] = array._elems[i]; + } + } + + Array(Array<T>&& array) noexcept + : _size(array._size) + , _elems(std::move(array._elems)) + {} + + Array<T>& operator=(Array<T>&& array) noexcept + { + _size = array._size; + _elems = std::move(array._elems); + return *this; + } + + Array(size_t size, const Array<T>& contents) + : _size(size) + , _elems(size ? new T[size] : nullptr) + { + assert(contents.size() >= size); + for (size_t i = 0; i < std::min(size, contents.size()); ++i) { + _elems[i] = contents[i]; + } + } + + Array(size_t size, const Array<T>& contents, T initial_value = T()) + : _size(size) + , _elems(size ? new T[size] : nullptr) + { + const size_t end = std::min(size, contents.size()); + for (size_t i = 0; i < end; ++i) { + _elems[i] = contents[i]; + } + for (size_t i = end; i < size; ++i) { + _elems[i] = initial_value; + } + } + + virtual void alloc(size_t num_elems) + { + _size = num_elems; + + if (num_elems > 0) { + _elems = std::unique_ptr<T[]>(new T[num_elems]); + } else { + _elems.reset(); + } + } + + virtual void alloc(size_t num_elems, T initial_value) + { + _size = num_elems; + + if (num_elems > 0) { + _elems = std::unique_ptr<T[]>(new T[num_elems]); + for (size_t i = 0; i < _size; ++i) { + _elems[i] = initial_value; + } + } else { + _elems.reset(); + } + } + + inline size_t size() const { return _size; } + + inline T& operator[](size_t i) const + { + assert(i < _size); + return _elems[i]; + } + + inline T& at(size_t i) const + { + assert(i < _size); + return _elems[i]; + } private: - size_t _size; - std::unique_ptr<T[]> _elems; + size_t _size; + std::unique_ptr<T[]> _elems; }; } // namespace Raul diff --git a/include/raul/Deletable.hpp b/include/raul/Deletable.hpp index 63a7a88..39262c8 100644 --- a/include/raul/Deletable.hpp +++ b/include/raul/Deletable.hpp @@ -25,15 +25,15 @@ namespace Raul { class Deletable { public: - Deletable() = default; + Deletable() = default; - Deletable(const Deletable&) = default; - Deletable& operator=(const Deletable&) = default; + Deletable(const Deletable&) = default; + Deletable& operator=(const Deletable&) = default; - Deletable(Deletable&&) = default; - Deletable& operator=(Deletable&&) = default; + Deletable(Deletable&&) = default; + Deletable& operator=(Deletable&&) = default; - virtual ~Deletable() = default; + virtual ~Deletable() = default; }; } // namespace Raul diff --git a/include/raul/DoubleBuffer.hpp b/include/raul/DoubleBuffer.hpp index 95ece0f..1c1ee69 100644 --- a/include/raul/DoubleBuffer.hpp +++ b/include/raul/DoubleBuffer.hpp @@ -32,67 +32,71 @@ namespace Raul { * \ingroup raul */ template<typename T> -class DoubleBuffer { +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; - - DoubleBuffer(DoubleBuffer&&) = delete; - DoubleBuffer& operator=(DoubleBuffer&&) = delete; - - ~DoubleBuffer() = default; - - 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; - } - - 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; - } + explicit DoubleBuffer(T val) + : _state(State::READ_WRITE) + { + _vals[0] = std::move(val); + } + + DoubleBuffer(const DoubleBuffer&) = delete; + DoubleBuffer& operator=(const DoubleBuffer&) = delete; + + DoubleBuffer(DoubleBuffer&&) = delete; + DoubleBuffer& operator=(DoubleBuffer&&) = delete; + + ~DoubleBuffer() = default; + + 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; + } + + if (transition(State::WRITE_READ, State::LOCK_READ)) { + // Locked _vals[0] for writing + _vals[0] = std::move(new_val); + _state.store(State::READ_WRITE, std::memory_order_release); + return true; + } + + return false; + } private: - enum class State { - READ_WRITE, ///< Read vals[0], Write vals[1] - READ_LOCK, ///< Read vals[0], Lock vals[1] - WRITE_READ, ///< Write vals[0], Read vals[1] - LOCK_READ ///< Lock vals[0], Read vals[1] - }; - - bool transition(State from, const State to) { - return _state.compare_exchange_strong( - from, to, std::memory_order_release, std::memory_order_relaxed); - } - - std::atomic<State> _state; - T _vals[2]; + enum class State { + READ_WRITE, ///< Read vals[0], Write vals[1] + READ_LOCK, ///< Read vals[0], Lock vals[1] + WRITE_READ, ///< Write vals[0], Read vals[1] + LOCK_READ ///< Lock vals[0], Read vals[1] + }; + + bool transition(State from, const State to) + { + return _state.compare_exchange_strong( + from, to, std::memory_order_release, std::memory_order_relaxed); + } + + std::atomic<State> _state; + T _vals[2]; }; } // namespace Raul diff --git a/include/raul/Exception.hpp b/include/raul/Exception.hpp index 40a4382..919ece2 100644 --- a/include/raul/Exception.hpp +++ b/include/raul/Exception.hpp @@ -23,16 +23,19 @@ namespace Raul { /** An exception (unexpected error). */ -class Exception : public std::exception { +class Exception : public std::exception +{ public: - // NOLINTNEXTLINE(modernize-use-override, hicpp-use-override) - const char* what() const noexcept final override { return _what.c_str(); } + // NOLINTNEXTLINE(modernize-use-override, hicpp-use-override) + const char* what() const noexcept final override { return _what.c_str(); } protected: - explicit Exception(std::string what) : _what(std::move(what)) {} + explicit Exception(std::string what) + : _what(std::move(what)) + {} private: - const std::string _what; + const std::string _what; }; } // namespace Raul diff --git a/include/raul/Maid.hpp b/include/raul/Maid.hpp index 1c1977f..9176d8b 100644 --- a/include/raul/Maid.hpp +++ b/include/raul/Maid.hpp @@ -38,118 +38,133 @@ namespace 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; - - Disposable(Disposable&&) = delete; - Disposable& operator=(Disposable&&) = delete; - - ~Disposable() override = default; - - private: - friend class Maid; - Disposable* _maid_next{}; - }; - - /** Disposable wrapper for any type. */ - template<typename T> - class Managed : public Raul::Maid::Disposable, public T - { - public: - template<typename... Args> - explicit Managed(Args&&... args) - : T(std::forward<Args>(args)...) - {} - }; - - /** Deleter for Disposable objects. */ - template<typename T> - class Disposer { - public: - explicit Disposer(Maid* maid) : _maid(maid) {} - - Disposer() = default; - - void operator()(T* obj) { - if (_maid) { _maid->dispose(obj); } - } - - private: - Maid* _maid{nullptr}; - }; - - /** A managed pointer that automatically disposes of its contents. - * - * This is a unique_ptr so that it is possible to statically verify that - * code is real-time safe. - */ - template<typename T> using managed_ptr = std::unique_ptr<T, Disposer<T>>; - - Maid() : _disposed(nullptr) {} - - Maid(const Maid&) = delete; - Maid& operator=(const Maid&) = delete; - - Maid(Maid&&) = delete; - Maid& operator=(Maid&&) = delete; - - inline ~Maid() { cleanup(); } - - /** Return false iff there is currently no garbage. */ - inline bool empty() const { - return !_disposed.load(std::memory_order_relaxed); - } - - /** Enqueue an object for deletion when cleanup() is called next. - * - * This is thread-safe, and real-time safe assuming reasonably low - * contention. - */ - inline void dispose(Disposable* obj) { - if (obj) { - // Atomically add obj to the head of the disposed list - do { - obj->_maid_next = _disposed.load(std::memory_order_relaxed); - } while (!_disposed.compare_exchange_weak( - obj->_maid_next, obj, - std::memory_order_release, - std::memory_order_relaxed)); - } - } - - /** Delete all disposed objects immediately. - * - * Obviously not real-time safe, but may be called while other threads are - * calling dispose(). - */ - inline void cleanup() { - // Atomically get the head of the disposed list - Disposable* const disposed = _disposed.exchange( - nullptr, std::memory_order_acquire); - - // Free the disposed list - for (Disposable* obj = disposed; obj;) { - Disposable* const next = obj->_maid_next; - delete obj; - obj = next; - } - } - - /** Make a unique_ptr that will dispose its object when dropped. */ - template<class T, class... Args> - managed_ptr<T> make_managed(Args&&... args) { - return std::unique_ptr<T, Disposer<T> >( - new T(std::forward<Args>(args)...), - Disposer<T>(this)); - } + /** 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; + + Disposable(Disposable&&) = delete; + Disposable& operator=(Disposable&&) = delete; + + ~Disposable() override = default; + + private: + friend class Maid; + Disposable* _maid_next{}; + }; + + /** Disposable wrapper for any type. */ + template<typename T> + class Managed + : public Raul::Maid::Disposable + , public T + { + public: + template<typename... Args> + explicit Managed(Args&&... args) + : T(std::forward<Args>(args)...) + {} + }; + + /** Deleter for Disposable objects. */ + template<typename T> + class Disposer + { + public: + explicit Disposer(Maid* maid) + : _maid(maid) + {} + + Disposer() = default; + + void operator()(T* obj) + { + if (_maid) { + _maid->dispose(obj); + } + } + + private: + Maid* _maid{nullptr}; + }; + + /** A managed pointer that automatically disposes of its contents. + * + * This is a unique_ptr so that it is possible to statically verify that + * code is real-time safe. + */ + template<typename T> + using managed_ptr = std::unique_ptr<T, Disposer<T>>; + + Maid() + : _disposed(nullptr) + {} + + Maid(const Maid&) = delete; + Maid& operator=(const Maid&) = delete; + + Maid(Maid&&) = delete; + Maid& operator=(Maid&&) = delete; + + inline ~Maid() { cleanup(); } + + /** Return false iff there is currently no garbage. */ + inline bool empty() const + { + return !_disposed.load(std::memory_order_relaxed); + } + + /** Enqueue an object for deletion when cleanup() is called next. + * + * This is thread-safe, and real-time safe assuming reasonably low + * contention. + */ + inline void dispose(Disposable* obj) + { + if (obj) { + // Atomically add obj to the head of the disposed list + do { + obj->_maid_next = _disposed.load(std::memory_order_relaxed); + } while (!_disposed.compare_exchange_weak(obj->_maid_next, + obj, + std::memory_order_release, + std::memory_order_relaxed)); + } + } + + /** Delete all disposed objects immediately. + * + * Obviously not real-time safe, but may be called while other threads are + * calling dispose(). + */ + inline void cleanup() + { + // Atomically get the head of the disposed list + Disposable* const disposed = + _disposed.exchange(nullptr, std::memory_order_acquire); + + // Free the disposed list + for (Disposable* obj = disposed; obj;) { + Disposable* const next = obj->_maid_next; + delete obj; + obj = next; + } + } + + /** Make a unique_ptr that will dispose its object when dropped. */ + template<class T, class... Args> + managed_ptr<T> make_managed(Args&&... args) + { + return std::unique_ptr<T, Disposer<T>>(new T(std::forward<Args>(args)...), + Disposer<T>(this)); + } private: - std::atomic<Disposable*> _disposed; + std::atomic<Disposable*> _disposed; }; template<typename T> diff --git a/include/raul/Noncopyable.hpp b/include/raul/Noncopyable.hpp index 5ab4d82..6cfd0ed 100644 --- a/include/raul/Noncopyable.hpp +++ b/include/raul/Noncopyable.hpp @@ -18,17 +18,18 @@ namespace Raul { -class Noncopyable { +class Noncopyable +{ public: - Noncopyable(const Noncopyable&) = delete; - const Noncopyable& operator=(const Noncopyable&) = delete; + Noncopyable(const Noncopyable&) = delete; + const Noncopyable& operator=(const Noncopyable&) = delete; - Noncopyable(Noncopyable&&) = delete; - Noncopyable& operator=(Noncopyable&&) = delete; + Noncopyable(Noncopyable&&) = delete; + Noncopyable& operator=(Noncopyable&&) = delete; protected: - Noncopyable() = default; - ~Noncopyable() = default; + Noncopyable() = default; + ~Noncopyable() = default; }; } // namespace Raul diff --git a/include/raul/Path.hpp b/include/raul/Path.hpp index f455049..8da985a 100644 --- a/include/raul/Path.hpp +++ b/include/raul/Path.hpp @@ -32,178 +32,195 @@ namespace Raul { * * @ingroup raul */ -class Path : public std::basic_string<char> { +class Path : public std::basic_string<char> +{ public: - /** Attempt to construct an invalid Path. */ - class BadPath : public Exception { - public: - explicit BadPath(const std::string& path) : Exception(path) {} - }; - - /** Construct an uninitialzed path, because the STL is annoying. */ - Path() : std::basic_string<char>("/") {} - - /** Construct a Path from a C++ string. - * - * This will throw an exception if `path` is invalid. To avoid this, use - * is_valid() first to check. - */ - explicit Path(const std::basic_string<char>& path) - : std::basic_string<char>(path) - { - if (!is_valid(path)) { - throw BadPath(path); - } - } - - /** Construct a Path from a C string. - * - * This will throw an exception if `path` is invalid. To avoid this, use - * is_valid() first to check. - */ - explicit Path(const char* path) - : std::basic_string<char>(path) - { - if (!is_valid(path)) { - throw BadPath(path); - } - } - - Path(const Path& path) = default; - Path& operator=(const Path& path) = default; - - Path(Path&& path) = default; - Path& operator=(Path&& path) = default; - - ~Path() = default; - - /** Return true iff `c` is a valid Path character. */ - static inline bool is_valid_char(char c) { - return c == '/' || Symbol::is_valid_char(c); - } - - /** Return true iff `str` is a valid Path. */ - static inline bool is_valid(const std::basic_string<char>& str) { - if (str.empty() || (str[0] != '/')) { - return false; // Must start with '/' - } - - if (str != "/" && *str.rbegin() == '/') { - return false; // Must not end with '/' except for the root - } - - for (size_t i = 1; i < str.length(); ++i) { - if (!is_valid_char(str[i])) { - return false; // All characters must be /, _, a-z, A-Z, 0-9 - } - - if (str[i - 1] == '/') { - if (str[i] == '/') { - return false; // Must not contain "//" - } - - 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; - } - - 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; - } - - 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)); - } + /** Attempt to construct an invalid Path. */ + class BadPath : public Exception + { + public: + explicit BadPath(const std::string& path) + : Exception(path) + {} + }; + + /** Construct an uninitialzed path, because the STL is annoying. */ + Path() + : std::basic_string<char>("/") + {} + + /** Construct a Path from a C++ string. + * + * This will throw an exception if `path` is invalid. To avoid this, use + * is_valid() first to check. + */ + explicit Path(const std::basic_string<char>& path) + : std::basic_string<char>(path) + { + if (!is_valid(path)) { + throw BadPath(path); + } + } + + /** Construct a Path from a C string. + * + * This will throw an exception if `path` is invalid. To avoid this, use + * is_valid() first to check. + */ + explicit Path(const char* path) + : std::basic_string<char>(path) + { + if (!is_valid(path)) { + throw BadPath(path); + } + } + + Path(const Path& path) = default; + Path& operator=(const Path& path) = default; + + Path(Path&& path) = default; + Path& operator=(Path&& path) = default; + + ~Path() = default; + + /** Return true iff `c` is a valid Path character. */ + static inline bool is_valid_char(char c) + { + return c == '/' || Symbol::is_valid_char(c); + } + + /** Return true iff `str` is a valid Path. */ + static inline bool is_valid(const std::basic_string<char>& str) + { + if (str.empty() || (str[0] != '/')) { + return false; // Must start with '/' + } + + if (str != "/" && *str.rbegin() == '/') { + return false; // Must not end with '/' except for the root + } + + for (size_t i = 1; i < str.length(); ++i) { + if (!is_valid_char(str[i])) { + return false; // All characters must be /, _, a-z, A-Z, 0-9 + } + + if (str[i - 1] == '/') { + if (str[i] == '/') { + return false; // Must not contain "//" + } + + 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; + } + + 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; + } + + 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 diff --git a/include/raul/Process.hpp b/include/raul/Process.hpp index d77c3f3..1c5deed 100644 --- a/include/raul/Process.hpp +++ b/include/raul/Process.hpp @@ -30,52 +30,53 @@ namespace Raul { class Process : Noncopyable { public: - /** Launch a sub process. - * - * @param argv List of arguments, where argv[0] is the command name. - * @return True on success. - */ - static bool launch(const char* const argv[]) { - // Use the same double fork() trick as JACK to prevent zombie children - const int child = fork(); - - if (child == 0) { - // (in child) - - // Close all nonstandard file descriptors - struct rlimit max_fds{}; - getrlimit(RLIMIT_NOFILE, &max_fds); - for (rlim_t fd = 3; fd < max_fds.rlim_cur; ++fd) { - close(static_cast<int>(fd)); - } - - // Fork child - const int grandchild = fork(); - switch (grandchild) { - case 0: - // (in grandchild) - setsid(); - execvp(argv[0], const_cast<char*const*>(argv)); - _exit(-1); - - case -1: - // Fork failed, there is no grandchild - _exit(-1); - - default: - // Fork succeeded, return grandchild PID - _exit(grandchild); - } - } else if (child < 0) { - // Fork failed, there is no child - return false; - } - - return true; - } + /** Launch a sub process. + * + * @param argv List of arguments, where argv[0] is the command name. + * @return True on success. + */ + static bool launch(const char* const argv[]) + { + // Use the same double fork() trick as JACK to prevent zombie children + const int child = fork(); + + if (child == 0) { + // (in child) + + // Close all nonstandard file descriptors + struct rlimit max_fds {}; + getrlimit(RLIMIT_NOFILE, &max_fds); + for (rlim_t fd = 3; fd < max_fds.rlim_cur; ++fd) { + close(static_cast<int>(fd)); + } + + // Fork child + const int grandchild = fork(); + switch (grandchild) { + case 0: + // (in grandchild) + setsid(); + execvp(argv[0], const_cast<char* const*>(argv)); + _exit(-1); + + case -1: + // Fork failed, there is no grandchild + _exit(-1); + + default: + // Fork succeeded, return grandchild PID + _exit(grandchild); + } + } else if (child < 0) { + // Fork failed, there is no child + return false; + } + + return true; + } private: - Process() = default; + Process() = default; }; } // namespace Raul diff --git a/include/raul/RingBuffer.hpp b/include/raul/RingBuffer.hpp index 6c61d38..5460de2 100644 --- a/include/raul/RingBuffer.hpp +++ b/include/raul/RingBuffer.hpp @@ -34,187 +34,198 @@ namespace Raul { @ingroup raul */ -class RingBuffer : public Noncopyable { +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); - } - - RingBuffer(const RingBuffer&) = delete; - RingBuffer& operator=(const RingBuffer&) = delete; - - RingBuffer(RingBuffer&&) = delete; - RingBuffer& operator=(RingBuffer&&) = delete; - - ~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; - } - - return 0; - } - - /** - Skip data in the RingBuffer (advance read head without reading). - */ - inline uint32_t skip(uint32_t size) { - const uint32_t r = _read_head; - const uint32_t w = _write_head; - if (read_space_internal(r, w) < size) { - return 0; - } - - std::atomic_thread_fence(std::memory_order_acquire); - _read_head = (r + size) & _size_mask; - return size; - } - - /** - Write data to the RingBuffer. - */ - inline uint32_t write(uint32_t size, const void* src) { - const uint32_t r = _read_head; - const uint32_t w = _write_head; - if (write_space_internal(r, w) < size) { - return 0; - } - - if (w + size <= _size) { - memcpy(&_buf[w], src, size); - std::atomic_thread_fence(std::memory_order_release); - _write_head = (w + size) & _size_mask; - } else { - const uint32_t this_size = _size - w; - assert(this_size < size); - assert(w + this_size <= _size); - memcpy(&_buf[w], src, this_size); - memcpy(&_buf[0], - static_cast<const char*>(src) + this_size, - size - this_size); - std::atomic_thread_fence(std::memory_order_release); - _write_head = size - this_size; - } - - return size; - } + /** + 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); + } + + RingBuffer(const RingBuffer&) = delete; + RingBuffer& operator=(const RingBuffer&) = delete; + + RingBuffer(RingBuffer&&) = delete; + RingBuffer& operator=(RingBuffer&&) = delete; + + ~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; + } + + return 0; + } + + /** + Skip data in the RingBuffer (advance read head without reading). + */ + inline uint32_t skip(uint32_t size) + { + const uint32_t r = _read_head; + const uint32_t w = _write_head; + if (read_space_internal(r, w) < size) { + return 0; + } + + std::atomic_thread_fence(std::memory_order_acquire); + _read_head = (r + size) & _size_mask; + return size; + } + + /** + Write data to the RingBuffer. + */ + inline uint32_t write(uint32_t size, const void* src) + { + const uint32_t r = _read_head; + const uint32_t w = _write_head; + if (write_space_internal(r, w) < size) { + return 0; + } + + if (w + size <= _size) { + memcpy(&_buf[w], src, size); + std::atomic_thread_fence(std::memory_order_release); + _write_head = (w + size) & _size_mask; + } else { + const uint32_t this_size = _size - w; + assert(this_size < size); + assert(w + this_size <= _size); + memcpy(&_buf[w], src, this_size); + memcpy( + &_buf[0], static_cast<const char*>(src) + this_size, size - this_size); + std::atomic_thread_fence(std::memory_order_release); + _write_head = size - this_size; + } + + return size; + } private: - static inline uint32_t next_power_of_two(uint32_t size) { - // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - size--; - size |= size >> 1U; - size |= size >> 2U; - size |= size >> 4U; - size |= size >> 8U; - size |= size >> 16U; - size++; - return size; - } - - inline uint32_t write_space_internal(uint32_t r, uint32_t w) const { - if (r == w) { - return _size - 1; - } - - if (r < w) { - return ((r - w + _size) & _size_mask) - 1; - } - - return (r - w) - 1; - } - - inline uint32_t read_space_internal(uint32_t r, uint32_t w) const { - if (r < w) { - return w - r; - } - - return (w - r + _size) & _size_mask; - } - - inline uint32_t peek_internal(uint32_t r, uint32_t w, - uint32_t size, void* dst) const { - if (read_space_internal(r, w) < size) { - return 0; - } - - if (r + size < _size) { - memcpy(dst, &_buf[r], size); - } else { - const uint32_t first_size = _size - r; - memcpy(dst, &_buf[r], first_size); - memcpy(static_cast<char*>(dst) + first_size, - &_buf[0], - size - first_size); - } - - return size; - } - - mutable uint32_t _write_head; ///< Read index into _buf - mutable uint32_t _read_head; ///< Write index into _buf - - const uint32_t _size; ///< Size (capacity) in bytes - const uint32_t _size_mask; ///< Mask for fast modulo - - const std::unique_ptr<char[]> _buf; ///< Contents + 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; + } + + if (r < w) { + return ((r - w + _size) & _size_mask) - 1; + } + + return (r - w) - 1; + } + + inline uint32_t read_space_internal(uint32_t r, uint32_t w) const + { + if (r < w) { + return w - r; + } + + return (w - r + _size) & _size_mask; + } + + inline uint32_t peek_internal(uint32_t r, + uint32_t w, + uint32_t size, + void* dst) const + { + if (read_space_internal(r, w) < size) { + return 0; + } + + if (r + size < _size) { + memcpy(dst, &_buf[r], size); + } else { + const uint32_t first_size = _size - r; + memcpy(dst, &_buf[r], first_size); + memcpy(static_cast<char*>(dst) + first_size, &_buf[0], size - first_size); + } + + return size; + } + + mutable uint32_t _write_head; ///< Read index into _buf + mutable uint32_t _read_head; ///< Write index into _buf + + const uint32_t _size; ///< Size (capacity) in bytes + const uint32_t _size_mask; ///< Mask for fast modulo + + const std::unique_ptr<char[]> _buf; ///< Contents }; } // namespace Raul diff --git a/include/raul/Semaphore.hpp b/include/raul/Semaphore.hpp index f87dac3..5e9303b 100644 --- a/include/raul/Semaphore.hpp +++ b/include/raul/Semaphore.hpp @@ -17,14 +17,14 @@ #define RAUL_SEMAPHORE_HPP #ifdef __APPLE__ -# include <mach/mach.h> +# include <mach/mach.h> #elif defined(_WIN32) -# define NOMINMAX -# include <windows.h> +# define NOMINMAX +# include <windows.h> #else -# include <cerrno> -# include <ctime> -# include <semaphore.h> +# include <cerrno> +# include <ctime> +# include <semaphore.h> #endif #include <chrono> @@ -47,58 +47,57 @@ namespace Raul { class Semaphore { public: - /** - Create a new semaphore. + /** + 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"); - } - } + 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(const Semaphore&) = delete; + inline Semaphore& operator=(const Semaphore&) = delete; - inline Semaphore(Semaphore&&) = delete; - inline Semaphore& operator=(Semaphore&&) = delete; + inline Semaphore(Semaphore&&) = delete; + inline Semaphore& operator=(Semaphore&&) = delete; - inline ~Semaphore() { - destroy(); - } + inline ~Semaphore() { destroy(); } - /** Destroy and reset to a new initial value. */ - inline void reset(unsigned initial) { - destroy(); - init(initial); - } + /** Destroy and reset to a new initial value. */ + inline void reset(unsigned initial) + { + destroy(); + init(initial); + } - /** Post/Increment/Signal */ - inline void post(); + /** Post/Increment/Signal */ + inline void post(); - /** Wait/Decrement. Return false on error. */ - inline bool wait(); + /** Wait/Decrement. Return false on error. */ + inline bool wait(); - /** Attempt Wait/Decrement. Return true iff decremented. */ - inline bool try_wait(); + /** Attempt Wait/Decrement. Return true iff decremented. */ + inline bool try_wait(); - /** Wait for at most `ms` milliseconds. Return true iff decremented. */ - template<class Rep, class Period> - inline bool timed_wait(const std::chrono::duration<Rep, Period>& wait); + /** Wait for at most `ms` milliseconds. Return true iff decremented. */ + template<class Rep, class Period> + inline bool timed_wait(const std::chrono::duration<Rep, Period>& wait); private: - inline bool init(unsigned initial); - inline void destroy(); + inline bool init(unsigned initial); + inline void destroy(); #if defined(__APPLE__) - semaphore_t _sem; // sem_t is a worthless broken mess on OSX + semaphore_t _sem; // sem_t is a worthless broken mess on OSX #elif defined(_WIN32) - HANDLE _sem; // types are overrated anyway + HANDLE _sem; // types are overrated anyway #else - sem_t _sem; + sem_t _sem; #endif }; @@ -107,52 +106,55 @@ private: inline bool Semaphore::init(unsigned initial) { - if (semaphore_create(mach_task_self(), &_sem, SYNC_POLICY_FIFO, int(initial))) { - return false; - } - return true; + 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); + semaphore_destroy(mach_task_self(), _sem); } inline void Semaphore::post() { - semaphore_signal(_sem); + semaphore_signal(_sem); } inline bool Semaphore::wait() { - if (semaphore_wait(_sem) != KERN_SUCCESS) { - return false; - } - return true; + 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; + const mach_timespec_t zero = {0, 0}; + return semaphore_timedwait(_sem, zero) == KERN_SUCCESS; } template<class Rep, class Period> inline bool Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait) { - namespace chr = std::chrono; + namespace chr = std::chrono; + + const chr::seconds sec(chr::duration_cast<chr::seconds>(wait)); + const chr::nanoseconds nsec(wait - sec); - const chr::seconds sec(chr::duration_cast<chr::seconds>(wait)); - const chr::nanoseconds nsec(wait - sec); + const mach_timespec_t t = {static_cast<unsigned>(sec.count()), + static_cast<int>(nsec.count())}; - const mach_timespec_t t = { static_cast<unsigned>(sec.count()), - static_cast<int>(nsec.count()) }; - return semaphore_timedwait(_sem, t) == KERN_SUCCESS; + return semaphore_timedwait(_sem, t) == KERN_SUCCESS; } #elif defined(_WIN32) @@ -160,109 +162,111 @@ Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait) inline bool Semaphore::init(unsigned initial) { - if (!(_sem = CreateSemaphore(NULL, (LONG)initial, LONG_MAX, NULL))) { - return false; - } - return true; + if (!(_sem = CreateSemaphore(NULL, (LONG)initial, LONG_MAX, NULL))) { + return false; + } + + return true; } inline void Semaphore::destroy() { - CloseHandle(_sem); + CloseHandle(_sem); } inline void Semaphore::post() { - ReleaseSemaphore(_sem, 1, NULL); + ReleaseSemaphore(_sem, 1, NULL); } inline bool Semaphore::wait() { - if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) { - return false; - } - return true; + if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) { + return false; + } + + return true; } inline bool Semaphore::try_wait() { - return WaitForSingleObject(_sem, 0) == WAIT_OBJECT_0; + return WaitForSingleObject(_sem, 0) == WAIT_OBJECT_0; } template<class Rep, class Period> inline bool Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait) { - namespace chr = std::chrono; + namespace chr = std::chrono; - const chr::milliseconds ms(chr::duration_cast<chr::milliseconds>(wait)); - return WaitForSingleObject( - _sem, static_cast<DWORD>(ms.count())) == WAIT_OBJECT_0; + const chr::milliseconds ms(chr::duration_cast<chr::milliseconds>(wait)); + return WaitForSingleObject(_sem, static_cast<DWORD>(ms.count())) == + WAIT_OBJECT_0; } -#else /* !defined(__APPLE__) && !defined(_WIN32) */ +#else /* !defined(__APPLE__) && !defined(_WIN32) */ inline bool Semaphore::init(unsigned initial) { - return !sem_init(&_sem, 0, initial); + return !sem_init(&_sem, 0, initial); } inline void Semaphore::destroy() { - sem_destroy(&_sem); + sem_destroy(&_sem); } inline void Semaphore::post() { - sem_post(&_sem); + 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; + 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); + return (sem_trywait(&_sem) == 0); } template<class Rep, class Period> inline bool Semaphore::timed_wait(const std::chrono::duration<Rep, Period>& wait) { - namespace chr = std::chrono; + namespace chr = std::chrono; - // Use clock_gettime to ensure sem_timedwait uses the same clock - struct timespec time{}; - clock_gettime(CLOCK_REALTIME, &time); + // 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 auto now(chr::seconds(time.tv_sec) + chr::nanoseconds(time.tv_nsec)); + const auto end(now + wait); - const chr::seconds end_sec(chr::duration_cast<chr::seconds>(end)); - const chr::nanoseconds end_nsec(end - end_sec); + const chr::seconds end_sec(chr::duration_cast<chr::seconds>(end)); + const chr::nanoseconds end_nsec(end - end_sec); - const struct timespec ts_end = { static_cast<time_t>(end_sec.count()), - static_cast<long>(end_nsec.count()) }; + const struct timespec ts_end = {static_cast<time_t>(end_sec.count()), + static_cast<long>(end_nsec.count())}; - return (sem_timedwait(&_sem, &ts_end) == 0); + return (sem_timedwait(&_sem, &ts_end) == 0); } #endif diff --git a/include/raul/Socket.hpp b/include/raul/Socket.hpp index 2a67fd3..7da3555 100644 --- a/include/raul/Socket.hpp +++ b/include/raul/Socket.hpp @@ -32,229 +32,228 @@ namespace Raul { /** A safe and simple interface for UNIX or TCP sockets. */ -class Socket : public Raul::Noncopyable { +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(Socket&&) = delete; - Socket& operator=(Socket&&) = delete; - - ~Socket(); - - /** Bind a server socket to an address. - * @param uri Address URI, e.g. unix:///tmp/foo, or tcp://hostname:1234. - * Use "*" as hostname to listen on all interfaces. - * @return True on success. - */ - bool bind(const std::string& uri); - - /** Connect a client socket to a server address. - * @param uri Address URI, e.g. unix:///tmp/foo or tcp://somehost:1234 - * @return True on success. - */ - bool connect(const std::string& uri); - - /** Mark server socket as passive to listen for incoming connections. - * @return True on success. - */ - bool listen(); - - /** Accept a connection. - * @return An new open socket for the connection. - */ - std::shared_ptr<Socket> accept(); - - /** Return the file descriptor for the socket. */ - int fd() const { return _sock; } - - const std::string& uri() const { return _uri; } - - /** Close the socket. */ - void close(); - - /** Shut down the socket. - * This terminates any connections associated with the sockets, and will - * (unlike close()) cause a poll on the socket to return. - */ - void shutdown(); + 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(Socket&&) = delete; + Socket& operator=(Socket&&) = delete; + + ~Socket(); + + /** Bind a server socket to an address. + * @param uri Address URI, e.g. unix:///tmp/foo, or tcp://hostname:1234. + * Use "*" as hostname to listen on all interfaces. + * @return True on success. + */ + bool bind(const std::string& uri); + + /** Connect a client socket to a server address. + * @param uri Address URI, e.g. unix:///tmp/foo or tcp://somehost:1234 + * @return True on success. + */ + bool connect(const std::string& uri); + + /** Mark server socket as passive to listen for incoming connections. + * @return True on success. + */ + bool listen(); + + /** Accept a connection. + * @return An new open socket for the connection. + */ + std::shared_ptr<Socket> accept(); + + /** Return the file descriptor for the socket. */ + int fd() const { return _sock; } + + const std::string& uri() const { return _uri; } + + /** Close the socket. */ + void close(); + + /** Shut down the socket. + * This terminates any connections associated with the sockets, and will + * (unlike close()) cause a poll on the socket to return. + */ + void shutdown(); private: - bool set_addr(const std::string& uri); + bool set_addr(const std::string& uri); - std::string _uri; - struct sockaddr* _addr; - socklen_t _addr_len; - Type _type; - int _sock; + std::string _uri; + struct sockaddr* _addr; + socklen_t _addr_len; + Type _type; + int _sock; }; #ifndef NI_MAXHOST -# define NI_MAXHOST 1025 +# 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) +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; - } + 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) +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() +inline Socket::~Socket() { - free(_addr); - close(); + free(_addr); + close(); } inline bool Socket::set_addr(const std::string& uri) { - free(_addr); - if (_type == Type::UNIX && uri.substr(0, strlen("unix://")) == "unix://") { - const std::string path = uri.substr(strlen("unix://")); - auto* uaddr = static_cast<struct sockaddr_un*>( - calloc(1, sizeof(struct sockaddr_un))); - uaddr->sun_family = AF_UNIX; - strncpy(uaddr->sun_path, path.c_str(), sizeof(uaddr->sun_path) - 1); - _uri = uri; - _addr = reinterpret_cast<sockaddr*>(uaddr); - _addr_len = sizeof(struct sockaddr_un); - return true; - } - - if (_type == Type::TCP && uri.find("://") != std::string::npos) { - const std::string authority = uri.substr(uri.find("://") + 3); - const size_t port_sep = authority.find(':'); - if (port_sep == std::string::npos) { - return false; - } - - std::string host = authority.substr(0, port_sep); - const std::string port = authority.substr(port_sep + 1); - if (host.empty() || host == "*") { - host = "0.0.0.0"; // INADDR_ANY - } - - struct addrinfo* ainfo = nullptr; - if (getaddrinfo(host.c_str(), port.c_str(), nullptr, &ainfo)) { - return false; - } - - _uri = uri; - _addr = static_cast<struct sockaddr*>(malloc(ainfo->ai_addrlen)); - _addr_len = ainfo->ai_addrlen; - memcpy(_addr, ainfo->ai_addr, ainfo->ai_addrlen); - freeaddrinfo(ainfo); - return true; - } - - return false; + free(_addr); + if (_type == Type::UNIX && uri.substr(0, strlen("unix://")) == "unix://") { + const std::string path = uri.substr(strlen("unix://")); + auto* uaddr = + static_cast<struct sockaddr_un*>(calloc(1, sizeof(struct sockaddr_un))); + uaddr->sun_family = AF_UNIX; + strncpy(uaddr->sun_path, path.c_str(), sizeof(uaddr->sun_path) - 1); + _uri = uri; + _addr = reinterpret_cast<sockaddr*>(uaddr); + _addr_len = sizeof(struct sockaddr_un); + return true; + } + + if (_type == Type::TCP && uri.find("://") != std::string::npos) { + const std::string authority = uri.substr(uri.find("://") + 3); + const size_t port_sep = authority.find(':'); + if (port_sep == std::string::npos) { + return false; + } + + std::string host = authority.substr(0, port_sep); + const std::string port = authority.substr(port_sep + 1); + if (host.empty() || host == "*") { + host = "0.0.0.0"; // INADDR_ANY + } + + struct addrinfo* ainfo = nullptr; + if (getaddrinfo(host.c_str(), port.c_str(), nullptr, &ainfo)) { + return false; + } + + _uri = uri; + _addr = static_cast<struct sockaddr*>(malloc(ainfo->ai_addrlen)); + _addr_len = ainfo->ai_addrlen; + memcpy(_addr, ainfo->ai_addr, ainfo->ai_addrlen); + freeaddrinfo(ainfo); + return true; + } + + return false; } inline bool Socket::bind(const std::string& uri) { - return set_addr(uri) && ::bind(_sock, _addr, _addr_len) != -1; + 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; + return set_addr(uri) && ::connect(_sock, _addr, _addr_len) != -1; } inline bool Socket::listen() // NOLINT(readability-make-member-function-const) { - return ::listen(_sock, 64) != -1; + return ::listen(_sock, 64) != -1; } inline std::shared_ptr<Socket> Socket::accept() { - socklen_t client_addr_len = _addr_len; - auto* const client_addr = - static_cast<struct sockaddr*>(calloc(1, client_addr_len)); - - const int conn = ::accept(_sock, client_addr, &client_addr_len); - if (conn == -1) { - free(client_addr); - return std::shared_ptr<Socket>(); - } - - std::string client_uri = _uri; - if (_type != Type::UNIX) { - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - if (!getnameinfo(client_addr, client_addr_len, - host, sizeof(host), serv, sizeof(serv), 0)) { - const std::string scheme = _uri.substr(0, _uri.find(':')); - client_uri = scheme + "://" + host + ":" + serv; - } - } - - return std::make_shared<Socket>( - _type, client_uri, client_addr, client_addr_len, conn); + socklen_t client_addr_len = _addr_len; + auto* const client_addr = + static_cast<struct sockaddr*>(calloc(1, client_addr_len)); + + const int conn = ::accept(_sock, client_addr, &client_addr_len); + if (conn == -1) { + free(client_addr); + return std::shared_ptr<Socket>(); + } + + std::string client_uri = _uri; + if (_type != Type::UNIX) { + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + if (!getnameinfo(client_addr, + client_addr_len, + host, + sizeof(host), + serv, + sizeof(serv), + 0)) { + const std::string scheme = _uri.substr(0, _uri.find(':')); + client_uri = scheme + "://" + host + ":" + serv; + } + } + + return std::make_shared<Socket>( + _type, client_uri, client_addr, client_addr_len, conn); } inline void Socket::close() { - if (_sock != -1) { - ::close(_sock); - _sock = -1; - } + if (_sock != -1) { + ::close(_sock); + _sock = -1; + } } inline void Socket::shutdown() // NOLINT(readability-make-member-function-const) { - if (_sock != -1) { - ::shutdown(_sock, SHUT_RDWR); - } + if (_sock != -1) { + ::shutdown(_sock, SHUT_RDWR); + } } -} // namespace Raul +} // namespace Raul -#endif // RAUL_SOCKET_HPP +#endif // RAUL_SOCKET_HPP diff --git a/include/raul/Symbol.hpp b/include/raul/Symbol.hpp index ff593ff..30917b5 100644 --- a/include/raul/Symbol.hpp +++ b/include/raul/Symbol.hpp @@ -34,96 +34,104 @@ namespace Raul { * * @ingroup raul */ -class Symbol : public std::basic_string<char> { +class Symbol : public std::basic_string<char> +{ public: - /** Attempt to construct an invalid Symbol. */ - class BadSymbol : public Exception { - public: - explicit BadSymbol(const std::string& symbol) : Exception(symbol) {} - }; - - /** Construct a Symbol from a C++ string. - * - * This will throw an exception if `symbol` is invalid. To avoid this, - * use is_valid() first to check. - */ - explicit Symbol(const std::basic_string<char>& symbol) - : std::basic_string<char>(symbol) - { - if (!is_valid(symbol)) { - throw BadSymbol(symbol); - } - } - - /** Construct a Symbol from a C string. - * - * This will throw an exception if `symbol` is invalid. To avoid this, - * use is_valid() first to check. - */ - explicit Symbol(const char* symbol) - : std::basic_string<char>(symbol) - { - if (!is_valid(symbol)) { - throw BadSymbol(symbol); - } - } - - Symbol(const Symbol& symbol) = default; - Symbol& operator=(const Symbol& symbol) = default; - - Symbol(Symbol&& symbol) = default; - Symbol& operator=(Symbol&& symbol) = default; - - ~Symbol() = default; - - /** Return true iff `c` is a valid Symbol start character. */ - static inline bool is_valid_start_char(char c) { - return (c == '_') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); - } - - /** Return true iff `c` is a valid Symbol character. */ - static inline bool is_valid_char(char c) { - return is_valid_start_char(c) || (c >= '0' && c <= '9'); - } - - /** Return true iff `str` is a valid Symbol. */ - static inline bool is_valid(const std::basic_string<char>& str) { - if (str.empty() || (str[0] >= '0' && str[0] <= '9')) { - return false; // Must start with a letter or underscore - } - - for (auto c : str) { - if (!is_valid_char(c)) { - return false; // All characters must be _, a-z, A-Z, 0-9 - } - } - - return true; - } - - /** Convert a string to a valid symbol. - * - * This will make a best effort at turning `str` into a complete, valid - * Symbol, and will always return one. - */ - static inline Symbol symbolify(const std::basic_string<char>& in) { - if (in.empty()) { - return Symbol("_"); - } - - std::basic_string<char> out(in); - for (size_t i = 0; i < in.length(); ++i) { - if (!is_valid_char(out[i])) { - out[i] = '_'; - } - } - - if (is_valid_start_char(out[0])) { - return Symbol(out); - } - - return Symbol(std::string("_") + out); - } + /** Attempt to construct an invalid Symbol. */ + class BadSymbol : public Exception + { + public: + explicit BadSymbol(const std::string& symbol) + : Exception(symbol) + {} + }; + + /** Construct a Symbol from a C++ string. + * + * This will throw an exception if `symbol` is invalid. To avoid this, + * use is_valid() first to check. + */ + explicit Symbol(const std::basic_string<char>& symbol) + : std::basic_string<char>(symbol) + { + if (!is_valid(symbol)) { + throw BadSymbol(symbol); + } + } + + /** Construct a Symbol from a C string. + * + * This will throw an exception if `symbol` is invalid. To avoid this, + * use is_valid() first to check. + */ + explicit Symbol(const char* symbol) + : std::basic_string<char>(symbol) + { + if (!is_valid(symbol)) { + throw BadSymbol(symbol); + } + } + + Symbol(const Symbol& symbol) = default; + Symbol& operator=(const Symbol& symbol) = default; + + Symbol(Symbol&& symbol) = default; + Symbol& operator=(Symbol&& symbol) = default; + + ~Symbol() = default; + + /** Return true iff `c` is a valid Symbol start character. */ + static inline bool is_valid_start_char(char c) + { + return (c == '_') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + /** Return true iff `c` is a valid Symbol character. */ + static inline bool is_valid_char(char c) + { + return is_valid_start_char(c) || (c >= '0' && c <= '9'); + } + + /** Return true iff `str` is a valid Symbol. */ + static inline bool is_valid(const std::basic_string<char>& str) + { + if (str.empty() || (str[0] >= '0' && str[0] <= '9')) { + return false; // Must start with a letter or underscore + } + + for (auto c : str) { + if (!is_valid_char(c)) { + return false; // All characters must be _, a-z, A-Z, 0-9 + } + } + + return true; + } + + /** Convert a string to a valid symbol. + * + * This will make a best effort at turning `str` into a complete, valid + * Symbol, and will always return one. + */ + static inline Symbol symbolify(const std::basic_string<char>& in) + { + if (in.empty()) { + return Symbol("_"); + } + + std::basic_string<char> out(in); + for (size_t i = 0; i < in.length(); ++i) { + if (!is_valid_char(out[i])) { + out[i] = '_'; + } + } + + if (is_valid_start_char(out[0])) { + return Symbol(out); + } + + return Symbol(std::string("_") + out); + } }; } // namespace Raul diff --git a/include/raul/TimeSlice.hpp b/include/raul/TimeSlice.hpp index 90eb7d4..3efa180 100644 --- a/include/raul/TimeSlice.hpp +++ b/include/raul/TimeSlice.hpp @@ -44,111 +44,122 @@ namespace Raul { * * \ingroup raul */ -class TimeSlice : public Noncopyable { +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 {real_unit(), beats.to_double() * 1/_beat_rate}; - } - - inline TimeStamp beats_to_ticks(TimeStamp beats) const { - return {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}; } + 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 {real_unit(), beats.to_double() * 1 / _beat_rate}; + } + + inline TimeStamp beats_to_ticks(TimeStamp beats) const + { + return {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 + 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 diff --git a/include/raul/TimeStamp.hpp b/include/raul/TimeStamp.hpp index 01cc294..0385ef3 100644 --- a/include/raul/TimeStamp.hpp +++ b/include/raul/TimeStamp.hpp @@ -28,44 +28,46 @@ namespace Raul { /** A type of time stamp * \ingroup raul */ -class TimeUnit { +class TimeUnit +{ public: - enum Type { - FRAMES, - BEATS, - SECONDS - }; - - /** `ppt` (parts per tick) is the sample rate for FRAMES, PPQN for BEATS, - * and ignored for SECONDS. - */ - inline TimeUnit(Type type, uint32_t ppt) - : _type(type) - , _ppt(ppt) - { - assert(type == SECONDS || ppt != 0); - _type = type; - _ppt = ppt; - } - - static inline TimeUnit frames(uint32_t srate) { return {FRAMES, srate}; } - static inline TimeUnit beats(uint32_t ppqn) { return {BEATS, ppqn}; } - static inline TimeUnit seconds() { return {BEATS, std::numeric_limits<uint32_t>::max()}; } - - inline Type type() const { return _type; } - inline uint32_t ppt() const { return _ppt; } - - inline bool operator==(const TimeUnit& rhs) const { - return (_type == rhs._type && _ppt == rhs._ppt); - } - - inline bool operator!=(const TimeUnit& rhs) const { - return (_type != rhs._type || _ppt != rhs._ppt); - } + enum Type { FRAMES, BEATS, SECONDS }; + + /** `ppt` (parts per tick) is the sample rate for FRAMES, PPQN for BEATS, + * and ignored for SECONDS. + */ + inline TimeUnit(Type type, uint32_t ppt) + : _type(type) + , _ppt(ppt) + { + assert(type == SECONDS || ppt != 0); + _type = type; + _ppt = ppt; + } + + static inline TimeUnit frames(uint32_t srate) { return {FRAMES, srate}; } + static inline TimeUnit beats(uint32_t ppqn) { return {BEATS, ppqn}; } + static inline TimeUnit seconds() + { + return {BEATS, std::numeric_limits<uint32_t>::max()}; + } + + inline Type type() const { return _type; } + inline uint32_t ppt() const { return _ppt; } + + inline bool operator==(const TimeUnit& rhs) const + { + return (_type == rhs._type && _ppt == rhs._ppt); + } + + inline bool operator!=(const TimeUnit& rhs) const + { + return (_type != rhs._type || _ppt != rhs._ppt); + } private: - Type _type; - uint32_t _ppt; + Type _type; + uint32_t _ppt; }; /** A real-time time stamp (possible units: frame, absolute (s), or beat). @@ -77,158 +79,170 @@ private: * * \ingroup raul */ -class TimeStamp { +class TimeStamp +{ public: - explicit TimeStamp(TimeUnit unit, uint32_t ticks = 0, uint32_t subticks = 0) - : _ticks(ticks) - , _subticks(subticks) - , _unit(unit) - {} - - inline TimeStamp(TimeUnit unit, double dec) - : _ticks(0u) - , _subticks(0u) - , _unit(unit) - { - dec = std::max(0.0, dec); - dec = std::min(double(std::numeric_limits<uint32_t>::max()), dec); - double integral = 0.0; - const double fractional = modf(dec, &integral); - _ticks = static_cast<uint32_t>(integral); - _subticks = static_cast<uint32_t>(fractional * unit.ppt()); - } - - TimeStamp(const TimeStamp&) = default; - TimeStamp& operator=(const TimeStamp&) = default; - - TimeStamp(TimeStamp&&) = default; - TimeStamp& operator=(TimeStamp&&) = default; - - ~TimeStamp() = default; - - inline TimeUnit unit() const { return _unit; } - inline uint32_t ticks() const { return _ticks; } - inline uint32_t subticks() const { return _subticks; } - - inline double to_double() const { - return _ticks + (_subticks / static_cast<double>(_unit.ppt())); - } - - inline bool is_zero() const { - return _ticks == 0 && _subticks == 0; - } - - inline TimeStamp& operator=(uint32_t ticks) { - _ticks = ticks; - _subticks = 0; - return *this; - } - - inline bool operator==(const TimeStamp& rhs) const { - return _ticks == rhs._ticks - && _subticks == rhs._subticks - && _unit == rhs._unit; - } - - inline bool operator!=(const TimeStamp& rhs) const { - return !operator==(rhs); - } - - inline bool operator<(const TimeStamp& rhs) const { - assert(_unit == rhs._unit); - return (_ticks < rhs._ticks - || (_ticks == rhs._ticks && _subticks < rhs._subticks)); - } - - inline bool operator>(const TimeStamp& rhs) const { - assert(_unit == rhs._unit); - return (_ticks > rhs._ticks - || (_ticks == rhs._ticks && _subticks > rhs._subticks)); - } - - inline bool operator<=(const TimeStamp& rhs) const { - return (*this == rhs) || ((*this) < rhs); - } - - inline bool operator>=(const TimeStamp& rhs) const { - return (*this == rhs) || ((*this) > rhs); - } - - inline TimeStamp& operator+=(const TimeStamp& rhs) { - assert(_unit == rhs._unit); - _ticks += rhs._ticks; - if (_subticks + rhs._subticks <= _unit.ppt()) { - _subticks += rhs._subticks; - } else if (rhs._subticks > 0) { - ++_ticks; - _subticks = rhs._subticks + _subticks - _unit.ppt(); - } - return *this; - } - - inline TimeStamp& operator-=(const TimeStamp& rhs) { - assert(_unit == rhs._unit); - assert(rhs <= *this); - _ticks -= rhs._ticks; - if (_subticks >= rhs._subticks) { - _subticks -= rhs._subticks; - } else if (rhs._subticks > 0) { - --_ticks; - _subticks = _unit.ppt() - (rhs._subticks - _subticks); - } - return *this; - } - - inline TimeStamp operator+(const TimeStamp& rhs) const { - assert(_unit == rhs._unit); - TimeStamp result = *this; - result += rhs; - return result; - } - - inline TimeStamp operator-(const TimeStamp& rhs) const { - assert(_unit == rhs._unit); - TimeStamp result = *this; - result -= rhs; - return result; - } + explicit TimeStamp(TimeUnit unit, uint32_t ticks = 0, uint32_t subticks = 0) + : _ticks(ticks) + , _subticks(subticks) + , _unit(unit) + {} + + inline TimeStamp(TimeUnit unit, double dec) + : _ticks(0u) + , _subticks(0u) + , _unit(unit) + { + dec = std::max(0.0, dec); + dec = std::min(double(std::numeric_limits<uint32_t>::max()), dec); + double integral = 0.0; + const double fractional = modf(dec, &integral); + _ticks = static_cast<uint32_t>(integral); + _subticks = static_cast<uint32_t>(fractional * unit.ppt()); + } + + TimeStamp(const TimeStamp&) = default; + TimeStamp& operator=(const TimeStamp&) = default; + + TimeStamp(TimeStamp&&) = default; + TimeStamp& operator=(TimeStamp&&) = default; + + ~TimeStamp() = default; + + inline TimeUnit unit() const { return _unit; } + inline uint32_t ticks() const { return _ticks; } + inline uint32_t subticks() const { return _subticks; } + + inline double to_double() const + { + return _ticks + (_subticks / static_cast<double>(_unit.ppt())); + } + + inline bool is_zero() const { return _ticks == 0 && _subticks == 0; } + + inline TimeStamp& operator=(uint32_t ticks) + { + _ticks = ticks; + _subticks = 0; + return *this; + } + + inline bool operator==(const TimeStamp& rhs) const + { + return _ticks == rhs._ticks && _subticks == rhs._subticks && + _unit == rhs._unit; + } + + inline bool operator!=(const TimeStamp& rhs) const + { + return !operator==(rhs); + } + + inline bool operator<(const TimeStamp& rhs) const + { + assert(_unit == rhs._unit); + return (_ticks < rhs._ticks || + (_ticks == rhs._ticks && _subticks < rhs._subticks)); + } + + inline bool operator>(const TimeStamp& rhs) const + { + assert(_unit == rhs._unit); + return (_ticks > rhs._ticks || + (_ticks == rhs._ticks && _subticks > rhs._subticks)); + } + + inline bool operator<=(const TimeStamp& rhs) const + { + return (*this == rhs) || ((*this) < rhs); + } + + inline bool operator>=(const TimeStamp& rhs) const + { + return (*this == rhs) || ((*this) > rhs); + } + + inline TimeStamp& operator+=(const TimeStamp& rhs) + { + assert(_unit == rhs._unit); + _ticks += rhs._ticks; + if (_subticks + rhs._subticks <= _unit.ppt()) { + _subticks += rhs._subticks; + } else if (rhs._subticks > 0) { + ++_ticks; + _subticks = rhs._subticks + _subticks - _unit.ppt(); + } + return *this; + } + + inline TimeStamp& operator-=(const TimeStamp& rhs) + { + assert(_unit == rhs._unit); + assert(rhs <= *this); + _ticks -= rhs._ticks; + if (_subticks >= rhs._subticks) { + _subticks -= rhs._subticks; + } else if (rhs._subticks > 0) { + --_ticks; + _subticks = _unit.ppt() - (rhs._subticks - _subticks); + } + return *this; + } + + inline TimeStamp operator+(const TimeStamp& rhs) const + { + assert(_unit == rhs._unit); + TimeStamp result = *this; + result += rhs; + return result; + } + + inline TimeStamp operator-(const TimeStamp& rhs) const + { + assert(_unit == rhs._unit); + TimeStamp result = *this; + result -= rhs; + return result; + } private: - uint32_t _ticks; - uint32_t _subticks; - TimeUnit _unit; + uint32_t _ticks; + uint32_t _subticks; + TimeUnit _unit; }; 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; + 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 { +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) - {} + explicit FrameStamp(uint32_t rate, uint32_t ticks = 0, uint32_t subticks = 0) + : TimeStamp(TimeUnit(TimeUnit::FRAMES, rate), ticks, subticks) + {} }; -class BeatStamp : public TimeStamp { +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) - {} + 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 |