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 | |
parent | 5c263125aeae87dcd4694a4d3a4781bda7247a00 (diff) | |
download | raul-bd0214b1da66225f410641692e89e492f668472a.tar.gz raul-bd0214b1da66225f410641692e89e492f668472a.tar.bz2 raul-bd0214b1da66225f410641692e89e492f668472a.zip |
Format all code with clang-format
-rw-r--r-- | .clang-format | 23 | ||||
-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 | ||||
-rw-r--r-- | test/array_test.cpp | 38 | ||||
-rw-r--r-- | test/build_test.cpp | 72 | ||||
-rw-r--r-- | test/double_buffer_test.cpp | 14 | ||||
-rw-r--r-- | test/maid_test.cpp | 163 | ||||
-rw-r--r-- | test/path_test.cpp | 139 | ||||
-rw-r--r-- | test/ringbuffer_test.cpp | 167 | ||||
-rw-r--r-- | test/sem_test.cpp | 57 | ||||
-rw-r--r-- | test/socket_test.cpp | 132 | ||||
-rw-r--r-- | test/symbol_test.cpp | 74 | ||||
-rw-r--r-- | test/thread_test.cpp | 32 | ||||
-rw-r--r-- | test/time_test.cpp | 32 |
26 files changed, 1907 insertions, 1786 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1d0cff9 --- /dev/null +++ b/.clang-format @@ -0,0 +1,23 @@ +--- +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlinesLeft: true +BasedOnStyle: Mozilla +BraceWrapping: + AfterNamespace: false + AfterClass: true + AfterEnum: false + AfterExternBlock: false + AfterFunction: true + AfterStruct: false + SplitEmptyFunction: false + SplitEmptyRecord: false +BreakBeforeBraces: Custom +Cpp11BracedListStyle: true +IndentCaseLabels: false +IndentPPDirectives: AfterHash +KeepEmptyLinesAtTheStartOfBlocks: false +SpacesInContainerLiterals: false +StatementMacros: + - _Pragma +... 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 diff --git a/test/array_test.cpp b/test/array_test.cpp index fcbc810..8053732 100644 --- a/test/array_test.cpp +++ b/test/array_test.cpp @@ -23,29 +23,29 @@ int main() { - Raul::Array<int> array1(32, 2); + Raul::Array<int> array1(32, 2); - array1[0] = 42; - assert(array1[0] == 42); - assert(array1[1] == 2); - assert(array1.size() == 32); + array1[0] = 42; + assert(array1[0] == 42); + assert(array1[1] == 2); + assert(array1.size() == 32); - array1.alloc(16, 0); - assert(array1[0] == 0); - assert(array1.at(0) == 0); - assert(array1.size() == 16); + array1.alloc(16, 0); + assert(array1[0] == 0); + assert(array1.at(0) == 0); + assert(array1.size() == 16); - array1.alloc(8, 0); - assert(array1.size() == 8); + array1.alloc(8, 0); + assert(array1.size() == 8); - Raul::Array<int> array2(array1); - for (size_t i = 0; i < array1.size(); ++i) { - assert(array2[i] == array1[i]); - } + Raul::Array<int> array2(array1); + for (size_t i = 0; i < array1.size(); ++i) { + assert(array2[i] == array1[i]); + } - Raul::Array<int> array3(8, 47); - assert(array3[0] == 47); - assert(array3.size() == 8); + Raul::Array<int> array3(8, 47); + assert(array3[0] == 47); + assert(array3.size() == 8); - return 0; + return 0; } diff --git a/test/build_test.cpp b/test/build_test.cpp index a03f776..69ddf36 100644 --- a/test/build_test.cpp +++ b/test/build_test.cpp @@ -27,59 +27,57 @@ #include "raul/TimeStamp.hpp" #ifndef _WIN32 -# include "raul/Process.hpp" -# include "raul/Socket.hpp" +# include "raul/Process.hpp" +# include "raul/Socket.hpp" #endif class DeletableThing : public Raul::Deletable -{ -}; +{}; class NonCopyableThing : public Raul::Noncopyable -{ -}; +{}; int main() { - Raul::Array<int> array; - DeletableThing deletable; - Raul::DoubleBuffer<int> double_buffer(0); - Raul::Maid maid; - NonCopyableThing non_copyable; - Raul::Path path; - Raul::RingBuffer ring_buffer(64u); - Raul::Semaphore semaphore(0u); - Raul::Symbol symbol("foo"); - Raul::TimeSlice time_slice(48000u, 960u, 120.0); + Raul::Array<int> array; + DeletableThing deletable; + Raul::DoubleBuffer<int> double_buffer(0); + Raul::Maid maid; + NonCopyableThing non_copyable; + Raul::Path path; + Raul::RingBuffer ring_buffer(64u); + Raul::Semaphore semaphore(0u); + Raul::Symbol symbol("foo"); + Raul::TimeSlice time_slice(48000u, 960u, 120.0); - Raul::TimeStamp time_stamp(Raul::TimeUnit(Raul::TimeUnit::BEATS, 960u)); + Raul::TimeStamp time_stamp(Raul::TimeUnit(Raul::TimeUnit::BEATS, 960u)); - try { - Raul::Symbol bad_symbol("not a valid symbol!"); - (void)bad_symbol; - } catch (const Raul::Exception&) { - } + try { + Raul::Symbol bad_symbol("not a valid symbol!"); + (void)bad_symbol; + } catch (const Raul::Exception&) { + } #ifndef _WIN32 - const char* cmd[] = {"echo"}; - Raul::Process::launch(cmd); + const char* cmd[] = {"echo"}; + Raul::Process::launch(cmd); - Raul::Socket socket(Raul::Socket::Type::UNIX); + Raul::Socket socket(Raul::Socket::Type::UNIX); - (void)socket; + (void)socket; #endif - (void)array; - (void)deletable; - (void)double_buffer; - (void)maid; - (void)non_copyable; - (void)path; - (void)ring_buffer; - (void)symbol; - (void)time_slice; - (void)time_stamp; + (void)array; + (void)deletable; + (void)double_buffer; + (void)maid; + (void)non_copyable; + (void)path; + (void)ring_buffer; + (void)symbol; + (void)time_slice; + (void)time_stamp; - return 0; + return 0; } diff --git a/test/double_buffer_test.cpp b/test/double_buffer_test.cpp index e1a4112..0f58ea2 100644 --- a/test/double_buffer_test.cpp +++ b/test/double_buffer_test.cpp @@ -22,15 +22,15 @@ int main() { - Raul::DoubleBuffer<int> db(0); + Raul::DoubleBuffer<int> db(0); - assert(db.get() == 0); + assert(db.get() == 0); - db.set(42); - assert(db.get() == 42); + db.set(42); + assert(db.get() == 42); - db.set(43); - assert(db.get() == 43); + db.set(43); + assert(db.get() == 43); - return 0; + return 0; } diff --git a/test/maid_test.cpp b/test/maid_test.cpp index 192960c..ee2ee7f 100644 --- a/test/maid_test.cpp +++ b/test/maid_test.cpp @@ -33,106 +33,111 @@ static const size_t n_junk_per_thread = 1U << 16U; static std::atomic<size_t> n_junk(0); static std::atomic<size_t> n_finished_threads(0); -class Junk : public Maid::Disposable { +class Junk : public Maid::Disposable +{ public: - explicit Junk(size_t v) : _val(v) { ++n_junk; } + explicit Junk(size_t v) + : _val(v) + { + ++n_junk; + } - Junk(const Junk&) = delete; - Junk& operator=(const Junk&) = delete; + Junk(const Junk&) = delete; + Junk& operator=(const Junk&) = delete; - Junk(Junk&&) = delete; - Junk& operator=(Junk&&) = delete; + Junk(Junk&&) = delete; + Junk& operator=(Junk&&) = delete; - ~Junk() override { --n_junk; } + ~Junk() override { --n_junk; } - size_t value() const { return _val; } + size_t value() const { return _val; } private: - size_t _val; + size_t _val; }; static void litter(Maid* maid) { - for (size_t i = 0; i < n_junk_per_thread; ++i) { - Maid::managed_ptr<Junk> a = maid->make_managed<Junk>(i); - assert(a->value() == i); - } + for (size_t i = 0; i < n_junk_per_thread; ++i) { + Maid::managed_ptr<Junk> a = maid->make_managed<Junk>(i); + assert(a->value() == i); + } - ++n_finished_threads; + ++n_finished_threads; } static void test() { - Maid maid; - - // Check basic single-threaded correctness - { - assert(n_junk == 0); - Maid::managed_ptr<Junk> a = maid.make_managed<Junk>(1U); - assert(n_junk == 1); - Maid::managed_ptr<Junk> b = maid.make_managed<Junk>(2U); - assert(n_junk == 2); - } - - maid.dispose(nullptr); // Mustn't crash - - // All referenes dropped, but deletion deferred - assert(n_junk == 2); - - // Trigger actual deletion - maid.cleanup(); - assert(n_junk == 0); - assert(maid.empty()); - - // Create some threads to produce garbage - std::vector<std::thread> litterers; - for (size_t i = 0; i < n_threads; ++i) { - litterers.emplace_back(litter, &maid); - } - - // Wait for some garbage to show up if necessary (unlikely) - size_t initial_n_junk = n_junk; - while (maid.empty()) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - initial_n_junk = n_junk; - } - - printf("Starting with %zu initial bits of junk\n", initial_n_junk); - - // Ensure we're actually cleaning things up concurrently - maid.cleanup(); - assert(n_junk != initial_n_junk); - - // Continue cleaning up as long as threads are running - size_t n_cleanup_calls = 1; - while (n_finished_threads < n_threads) { - maid.cleanup(); - ++n_cleanup_calls; - } - - printf("Called cleanup %zu times\n", n_cleanup_calls); - - // Join litterer threads - for (auto& t : litterers) { - t.join(); - } - - // Clean up any leftover garbage (unlikely/impossible?) - maid.cleanup(); - assert(n_junk == 0); - - // Allocate a new object, then let it and the Maid go out of scope - Maid::managed_ptr<Junk> c = maid.make_managed<Junk>(5U); - assert(n_junk == 1); + Maid maid; + + // Check basic single-threaded correctness + { + assert(n_junk == 0); + Maid::managed_ptr<Junk> a = maid.make_managed<Junk>(1U); + assert(n_junk == 1); + Maid::managed_ptr<Junk> b = maid.make_managed<Junk>(2U); + assert(n_junk == 2); + } + + maid.dispose(nullptr); // Mustn't crash + + // All referenes dropped, but deletion deferred + assert(n_junk == 2); + + // Trigger actual deletion + maid.cleanup(); + assert(n_junk == 0); + assert(maid.empty()); + + // Create some threads to produce garbage + std::vector<std::thread> litterers; + for (size_t i = 0; i < n_threads; ++i) { + litterers.emplace_back(litter, &maid); + } + + // Wait for some garbage to show up if necessary (unlikely) + size_t initial_n_junk = n_junk; + while (maid.empty()) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + initial_n_junk = n_junk; + } + + printf("Starting with %zu initial bits of junk\n", initial_n_junk); + + // Ensure we're actually cleaning things up concurrently + maid.cleanup(); + assert(n_junk != initial_n_junk); + + // Continue cleaning up as long as threads are running + size_t n_cleanup_calls = 1; + while (n_finished_threads < n_threads) { + maid.cleanup(); + ++n_cleanup_calls; + } + + printf("Called cleanup %zu times\n", n_cleanup_calls); + + // Join litterer threads + for (auto& t : litterers) { + t.join(); + } + + // Clean up any leftover garbage (unlikely/impossible?) + maid.cleanup(); + assert(n_junk == 0); + + // Allocate a new object, then let it and the Maid go out of scope + Maid::managed_ptr<Junk> c = maid.make_managed<Junk>(5U); + assert(n_junk == 1); } int main() { - assert(n_junk == 0); - test(); - assert(n_junk == 0); - return 0; + assert(n_junk == 0); + test(); + assert(n_junk == 0); + return 0; } diff --git a/test/path_test.cpp b/test/path_test.cpp index deda557..5681ec1 100644 --- a/test/path_test.cpp +++ b/test/path_test.cpp @@ -26,73 +26,74 @@ int main() { - using Path = Raul::Path; - using Symbol = Raul::Symbol; - - assert(Path("/foo/bar").parent() == Path("/foo")); - assert(Path("/foo").parent() == Path("/")); - assert(Path("/").parent() == Path("/")); - - assert(Path("/").is_parent_of(Path("/foo"))); - assert(Path("/foo").is_parent_of(Path("/foo/bar"))); - assert(!(Path("/foo").is_parent_of(Path("/foo2")))); - assert(!(Path("/foo").is_parent_of(Path("/foo")))); - - assert(Path::lca(Path("/foo"), Path("/foo/bar/baz")) == Path("/")); - assert(Path::lca(Path("/foo/bar"), Path("/foo/bar/baz")) == Path("/foo")); - assert(Path::lca(Path("/foo/bar/quux"), Path("/foo/bar/baz")) == Path("/foo/bar")); - - assert(!Path::is_valid("")); - assert(!Path::is_valid("hello")); - assert(!Path::is_valid("/foo/bar/")); - assert(!Path::is_valid("/foo//bar")); - assert(!Path::is_valid("/foo/bar/d*s")); - assert(Path::is_valid("/")); - assert(!Path::is_valid("/foo/3foo/bar")); - - assert(Path::descendant_comparator(Path("/"), Path("/"))); - assert(Path::descendant_comparator(Path("/"), Path("/foo"))); - assert(Path::descendant_comparator(Path("/foo"), Path("/foo/bar"))); - assert(Path::descendant_comparator(Path("/foo"), Path("/foo"))); - assert(Path::descendant_comparator(Path("/"), Path("/"))); - assert(!Path::descendant_comparator(Path("/baz"), Path("/"))); - assert(!Path::descendant_comparator(Path("/foo"), Path("/bar"))); - assert(!Path::descendant_comparator(Path("/foo/bar"), Path("/foo"))); - - assert(!Symbol::is_valid("")); - assert(!Symbol::is_valid("/I/have/slashes")); - assert(!Symbol::is_valid("!illegalchar")); - assert(!Symbol::is_valid("0illegalleadingdigit")); - assert(strcmp(Symbol::symbolify("").c_str(), "")); - - assert(Path("/foo").child(Symbol("bar")) == "/foo/bar"); - assert(Path("/foo").child(Path("/bar/baz")) == "/foo/bar/baz"); - assert(Path("/foo").child(Path("/")) == "/foo"); - - assert(!strcmp(Path("/foo").symbol(), "foo")); - assert(!strcmp(Path("/foo/bar").symbol(), "bar")); - assert(!strcmp(Path("/").symbol(), "")); - - const Path original(std::string("/foo/bar")); - assert(original == original); - - bool valid = true; - try { - Path path("/ends/in/slash/"); - } catch (const Path::BadPath& e) { - std::cerr << "Caught exception: " << e.what() << std::endl; - valid = false; - } - assert(!valid); - - valid = true; - try { - Path path(std::string("/has//double/slash")); - } catch (const Path::BadPath& e) { - std::cerr << "Caught exception: " << e.what() << std::endl; - valid = false; - } - assert(!valid); - - return 0; + using Path = Raul::Path; + using Symbol = Raul::Symbol; + + assert(Path("/foo/bar").parent() == Path("/foo")); + assert(Path("/foo").parent() == Path("/")); + assert(Path("/").parent() == Path("/")); + + assert(Path("/").is_parent_of(Path("/foo"))); + assert(Path("/foo").is_parent_of(Path("/foo/bar"))); + assert(!(Path("/foo").is_parent_of(Path("/foo2")))); + assert(!(Path("/foo").is_parent_of(Path("/foo")))); + + assert(Path::lca(Path("/foo"), Path("/foo/bar/baz")) == Path("/")); + assert(Path::lca(Path("/foo/bar"), Path("/foo/bar/baz")) == Path("/foo")); + assert(Path::lca(Path("/foo/bar/quux"), Path("/foo/bar/baz")) == + Path("/foo/bar")); + + assert(!Path::is_valid("")); + assert(!Path::is_valid("hello")); + assert(!Path::is_valid("/foo/bar/")); + assert(!Path::is_valid("/foo//bar")); + assert(!Path::is_valid("/foo/bar/d*s")); + assert(Path::is_valid("/")); + assert(!Path::is_valid("/foo/3foo/bar")); + + assert(Path::descendant_comparator(Path("/"), Path("/"))); + assert(Path::descendant_comparator(Path("/"), Path("/foo"))); + assert(Path::descendant_comparator(Path("/foo"), Path("/foo/bar"))); + assert(Path::descendant_comparator(Path("/foo"), Path("/foo"))); + assert(Path::descendant_comparator(Path("/"), Path("/"))); + assert(!Path::descendant_comparator(Path("/baz"), Path("/"))); + assert(!Path::descendant_comparator(Path("/foo"), Path("/bar"))); + assert(!Path::descendant_comparator(Path("/foo/bar"), Path("/foo"))); + + assert(!Symbol::is_valid("")); + assert(!Symbol::is_valid("/I/have/slashes")); + assert(!Symbol::is_valid("!illegalchar")); + assert(!Symbol::is_valid("0illegalleadingdigit")); + assert(strcmp(Symbol::symbolify("").c_str(), "")); + + assert(Path("/foo").child(Symbol("bar")) == "/foo/bar"); + assert(Path("/foo").child(Path("/bar/baz")) == "/foo/bar/baz"); + assert(Path("/foo").child(Path("/")) == "/foo"); + + assert(!strcmp(Path("/foo").symbol(), "foo")); + assert(!strcmp(Path("/foo/bar").symbol(), "bar")); + assert(!strcmp(Path("/").symbol(), "")); + + const Path original(std::string("/foo/bar")); + assert(original == original); + + bool valid = true; + try { + Path path("/ends/in/slash/"); + } catch (const Path::BadPath& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + assert(!valid); + + valid = true; + try { + Path path(std::string("/has//double/slash")); + } catch (const Path::BadPath& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + assert(!valid); + + return 0; } diff --git a/test/ringbuffer_test.cpp b/test/ringbuffer_test.cpp index 2a83912..6bf32ce 100644 --- a/test/ringbuffer_test.cpp +++ b/test/ringbuffer_test.cpp @@ -34,66 +34,67 @@ namespace { using RingBuffer = Raul::RingBuffer; struct Context { - std::unique_ptr<RingBuffer> ring{}; - size_t n_writes{0}; + std::unique_ptr<RingBuffer> ring{}; + size_t n_writes{0}; }; int gen_msg(int* msg, int start) { - for (unsigned i = 0u; i < MSG_SIZE; ++i) { - msg[i] = start; - start = (start + 1) % INT_MAX; - } - return start; + for (unsigned i = 0u; i < MSG_SIZE; ++i) { + msg[i] = start; + start = (start + 1) % INT_MAX; + } + return start; } void check_msg(const int* msg1, const int* msg2) { - for (unsigned i = 0u; i < MSG_SIZE; ++i) { - assert(msg1[i] == msg2[i]); - } + for (unsigned i = 0u; i < MSG_SIZE; ++i) { + assert(msg1[i] == msg2[i]); + } } void reader(Context& ctx) { - printf("Reader starting\n"); - - int ref_msg[MSG_SIZE]; // Reference generated for comparison - int read_msg[MSG_SIZE]; // Read from ring - size_t count = 0; - int start = gen_msg(ref_msg, 0); - for (size_t i = 0; i < ctx.n_writes; ++i) { - if (ctx.ring->read_space() >= MSG_SIZE * sizeof(int)) { - const uint32_t n_read = ctx.ring->read(MSG_SIZE * sizeof(int), read_msg); - assert(n_read == MSG_SIZE * sizeof(int)); - check_msg(ref_msg, read_msg); - start = gen_msg(ref_msg, start); - ++count; - } - } - - printf("Reader finished\n"); + printf("Reader starting\n"); + + int ref_msg[MSG_SIZE]; // Reference generated for comparison + int read_msg[MSG_SIZE]; // Read from ring + size_t count = 0; + int start = gen_msg(ref_msg, 0); + for (size_t i = 0; i < ctx.n_writes; ++i) { + if (ctx.ring->read_space() >= MSG_SIZE * sizeof(int)) { + const uint32_t n_read = ctx.ring->read(MSG_SIZE * sizeof(int), read_msg); + assert(n_read == MSG_SIZE * sizeof(int)); + check_msg(ref_msg, read_msg); + start = gen_msg(ref_msg, start); + ++count; + } + } + + printf("Reader finished\n"); } void writer(Context& ctx) { - printf("Writer starting\n"); - - int write_msg[MSG_SIZE]; // Written to ring - int start = gen_msg(write_msg, 0); - for (size_t i = 0; i < ctx.n_writes; ++i) { - if (ctx.ring->write_space() >= MSG_SIZE * sizeof(int)) { - const uint32_t n_write = ctx.ring->write(MSG_SIZE * sizeof(int), write_msg); - assert(n_write == MSG_SIZE * sizeof(int)); - start = gen_msg(write_msg, start); - } - } - - printf("Writer finished\n"); + printf("Writer starting\n"); + + int write_msg[MSG_SIZE]; // Written to ring + int start = gen_msg(write_msg, 0); + for (size_t i = 0; i < ctx.n_writes; ++i) { + if (ctx.ring->write_space() >= MSG_SIZE * sizeof(int)) { + const uint32_t n_write = + ctx.ring->write(MSG_SIZE * sizeof(int), write_msg); + assert(n_write == MSG_SIZE * sizeof(int)); + start = gen_msg(write_msg, start); + } + } + + printf("Writer finished\n"); } } // namespace @@ -101,62 +102,64 @@ writer(Context& ctx) int main(int argc, char** argv) { - if (argc > 1 && argv[1][0] == '-') { - printf("Usage: %s SIZE N_WRITES\n", argv[0]); - return 1; - } + if (argc > 1 && argv[1][0] == '-') { + printf("Usage: %s SIZE N_WRITES\n", argv[0]); + return 1; + } - Context ctx; + Context ctx; - uint32_t size = 512u; - if (argc > 1) { - size = static_cast<uint32_t>(std::stoi(argv[1])); - } + uint32_t size = 512u; + if (argc > 1) { + size = static_cast<uint32_t>(std::stoi(argv[1])); + } - ctx.n_writes = size * 1024u; - if (argc > 2) { - ctx.n_writes = std::stoul(argv[2]); - } + ctx.n_writes = size * 1024u; + if (argc > 2) { + ctx.n_writes = std::stoul(argv[2]); + } - printf("Testing %zu writes of %u ints to a %u int ring...\n", - ctx.n_writes, MSG_SIZE, size); + printf("Testing %zu writes of %u ints to a %u int ring...\n", + ctx.n_writes, + MSG_SIZE, + size); - ctx.ring = std::unique_ptr<RingBuffer>(new RingBuffer(size)); + ctx.ring = std::unique_ptr<RingBuffer>(new RingBuffer(size)); - auto& ring = ctx.ring; - assert(ring->capacity() >= size - 1); + auto& ring = ctx.ring; + assert(ring->capacity() >= size - 1); - assert(!ring->skip(1)); + assert(!ring->skip(1)); - char buf[6] = { 'h', 'e', 'l', 'l', '0', '\0' }; - assert(!ring->read(1, buf)); + char buf[6] = {'h', 'e', 'l', 'l', '0', '\0'}; + assert(!ring->read(1, buf)); - ring->write(sizeof(buf), buf); - ring->skip(1); - char buf2[sizeof(buf) - 1]; - ring->read(sizeof(buf2), buf2); - assert(!strcmp(buf2, buf + 1)); + ring->write(sizeof(buf), buf); + ring->skip(1); + char buf2[sizeof(buf) - 1]; + ring->read(sizeof(buf2), buf2); + assert(!strcmp(buf2, buf + 1)); - ring->reset(); - assert(ring->read_space() == 0); + ring->reset(); + assert(ring->read_space() == 0); - for (uint32_t i = 0; i < ring->capacity(); ++i) { - const char c = 'X'; - assert(ring->write(1, &c) == 1); - } + for (uint32_t i = 0; i < ring->capacity(); ++i) { + const char c = 'X'; + assert(ring->write(1, &c) == 1); + } - assert(ring->write_space() == 0); - assert(ring->write(1, buf) == 0); - assert(ring->peek(1, buf2) == 1); - assert(buf2[0] == 'X'); + assert(ring->write_space() == 0); + assert(ring->write(1, buf) == 0); + assert(ring->peek(1, buf2) == 1); + assert(buf2[0] == 'X'); - ring->reset(); + ring->reset(); - std::thread reader_thread(reader, std::ref(ctx)); - std::thread writer_thread(writer, std::ref(ctx)); + std::thread reader_thread(reader, std::ref(ctx)); + std::thread writer_thread(writer, std::ref(ctx)); - reader_thread.join(); - writer_thread.join(); + reader_thread.join(); + writer_thread.join(); - return 0; + return 0; } diff --git a/test/sem_test.cpp b/test/sem_test.cpp index 58bc56c..2e8f8f8 100644 --- a/test/sem_test.cpp +++ b/test/sem_test.cpp @@ -24,48 +24,49 @@ static void wait_for_sem(Raul::Semaphore* sem) { - sem->wait(); + sem->wait(); } static void timed_wait_for_sem(Raul::Semaphore* sem) { - while (!sem->timed_wait(std::chrono::milliseconds(100))) {} + while (!sem->timed_wait(std::chrono::milliseconds(100))) { + } } int main() { - Raul::Semaphore sem(0); - assert(!sem.try_wait()); + Raul::Semaphore sem(0); + assert(!sem.try_wait()); - // Check that semaphore wakes up strict waiter - std::thread waiter(wait_for_sem, &sem); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - sem.post(); - waiter.join(); + // Check that semaphore wakes up strict waiter + std::thread waiter(wait_for_sem, &sem); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + sem.post(); + waiter.join(); - // Check that semaphore wakes up timed waiter - std::thread timed_waiter(timed_wait_for_sem, &sem); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - sem.post(); - timed_waiter.join(); + // Check that semaphore wakes up timed waiter + std::thread timed_waiter(timed_wait_for_sem, &sem); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + sem.post(); + timed_waiter.join(); - // Check that timed_wait actually waits - const auto start = std::chrono::steady_clock::now(); - sem.timed_wait(std::chrono::milliseconds(100)); - const auto end = std::chrono::steady_clock::now(); - assert(end - start > std::chrono::milliseconds(80)); - assert(end - start < std::chrono::milliseconds(400)); + // Check that timed_wait actually waits + const auto start = std::chrono::steady_clock::now(); + sem.timed_wait(std::chrono::milliseconds(100)); + const auto end = std::chrono::steady_clock::now(); + assert(end - start > std::chrono::milliseconds(80)); + assert(end - start < std::chrono::milliseconds(400)); - // Check that we can't successfully wait on a zero semaphore - assert(!sem.timed_wait(std::chrono::milliseconds(100))); + // Check that we can't successfully wait on a zero semaphore + assert(!sem.timed_wait(std::chrono::milliseconds(100))); - // Check that initial value works correctly - Raul::Semaphore sem2(2); - assert(sem2.wait()); - assert(sem2.wait()); - assert(!sem2.try_wait()); + // Check that initial value works correctly + Raul::Semaphore sem2(2); + assert(sem2.wait()); + assert(sem2.wait()); + assert(!sem2.try_wait()); - return 0; + return 0; } diff --git a/test/socket_test.cpp b/test/socket_test.cpp index f2f04fc..e1638d6 100644 --- a/test/socket_test.cpp +++ b/test/socket_test.cpp @@ -29,70 +29,70 @@ int main() { - using Socket = Raul::Socket; - - std::string unix_uri("unix:///tmp/raul_test_sock"); - std::string tcp_uri("tcp://127.0.0.1:12345"); - - Socket unix_server_sock(Socket::Type::UNIX); - Socket tcp_server_sock(Socket::Type::TCP); - assert(unix_server_sock.bind(unix_uri)); - assert(unix_server_sock.listen()); - assert(tcp_server_sock.bind(tcp_uri)); - assert(tcp_server_sock.listen()); - - const pid_t child_pid = fork(); - if (child_pid) { - // This is the parent (server) process - int status = 0; - waitpid(child_pid, &status, 0); - - struct pollfd pfds[2]; - pfds[0].fd = unix_server_sock.fd(); - pfds[0].events = POLLIN; - pfds[0].revents = 0; - pfds[1].fd = tcp_server_sock.fd(); - pfds[1].events = POLLIN; - pfds[1].revents = 0; - - unsigned n_received = 0; - while (n_received < 2) { - const int ret = poll(pfds, 2, -1); - assert(ret != -1); - - if ((pfds[0].revents & POLLHUP) || pfds[1].revents & POLLHUP) { - break; - } - - if (ret == 0) { - fprintf(stderr, "poll returned with no data\n"); - continue; - } - - if (pfds[0].revents & POLLIN) { - std::shared_ptr<Socket> conn = unix_server_sock.accept(); - ++n_received; - } - - if (pfds[1].revents & POLLIN) { - std::shared_ptr<Socket> conn = tcp_server_sock.accept(); - ++n_received; - } - } - - unix_server_sock.shutdown(); - tcp_server_sock.shutdown(); - unlink("/tmp/raul_test_sock"); - fprintf(stderr, "n received: %u\n", n_received); - return n_received != 2; - } - - // This is the child (client) process - Raul::Socket unix_sock(Socket::Type::UNIX); - Raul::Socket tcp_sock(Socket::Type::TCP); - - assert(unix_sock.connect(unix_uri)); - assert(tcp_sock.connect(tcp_uri)); - - return 0; + using Socket = Raul::Socket; + + std::string unix_uri("unix:///tmp/raul_test_sock"); + std::string tcp_uri("tcp://127.0.0.1:12345"); + + Socket unix_server_sock(Socket::Type::UNIX); + Socket tcp_server_sock(Socket::Type::TCP); + assert(unix_server_sock.bind(unix_uri)); + assert(unix_server_sock.listen()); + assert(tcp_server_sock.bind(tcp_uri)); + assert(tcp_server_sock.listen()); + + const pid_t child_pid = fork(); + if (child_pid) { + // This is the parent (server) process + int status = 0; + waitpid(child_pid, &status, 0); + + struct pollfd pfds[2]; + pfds[0].fd = unix_server_sock.fd(); + pfds[0].events = POLLIN; + pfds[0].revents = 0; + pfds[1].fd = tcp_server_sock.fd(); + pfds[1].events = POLLIN; + pfds[1].revents = 0; + + unsigned n_received = 0; + while (n_received < 2) { + const int ret = poll(pfds, 2, -1); + assert(ret != -1); + + if ((pfds[0].revents & POLLHUP) || pfds[1].revents & POLLHUP) { + break; + } + + if (ret == 0) { + fprintf(stderr, "poll returned with no data\n"); + continue; + } + + if (pfds[0].revents & POLLIN) { + std::shared_ptr<Socket> conn = unix_server_sock.accept(); + ++n_received; + } + + if (pfds[1].revents & POLLIN) { + std::shared_ptr<Socket> conn = tcp_server_sock.accept(); + ++n_received; + } + } + + unix_server_sock.shutdown(); + tcp_server_sock.shutdown(); + unlink("/tmp/raul_test_sock"); + fprintf(stderr, "n received: %u\n", n_received); + return n_received != 2; + } + + // This is the child (client) process + Raul::Socket unix_sock(Socket::Type::UNIX); + Raul::Socket tcp_sock(Socket::Type::TCP); + + assert(unix_sock.connect(unix_uri)); + assert(tcp_sock.connect(tcp_uri)); + + return 0; } diff --git a/test/symbol_test.cpp b/test/symbol_test.cpp index 16d9476..e52a294 100644 --- a/test/symbol_test.cpp +++ b/test/symbol_test.cpp @@ -25,47 +25,47 @@ int main() { - using Symbol = Raul::Symbol; + using Symbol = Raul::Symbol; - std::list<std::string> names; - names.emplace_back("Dry/Wet Balance"); - names.emplace_back("foo+1bar(baz)"); - names.emplace_back("ThisCRAR"); - names.emplace_back("NAME"); - names.emplace_back("thing with a bunch of spaces"); - names.emplace_back("thing-with-a-bunch-of-dashes"); - names.emplace_back("CamelCaseABC"); - names.emplace_back("Signal Level [dB]"); - names.emplace_back("Gain dB"); - names.emplace_back("Dry/Wet Balance"); - names.emplace_back("Phaser1 - Similar to CSound's phaser1 by Sean Costello"); - names.emplace_back("1"); - names.emplace_back(""); + std::list<std::string> names; + names.emplace_back("Dry/Wet Balance"); + names.emplace_back("foo+1bar(baz)"); + names.emplace_back("ThisCRAR"); + names.emplace_back("NAME"); + names.emplace_back("thing with a bunch of spaces"); + names.emplace_back("thing-with-a-bunch-of-dashes"); + names.emplace_back("CamelCaseABC"); + names.emplace_back("Signal Level [dB]"); + names.emplace_back("Gain dB"); + names.emplace_back("Dry/Wet Balance"); + names.emplace_back("Phaser1 - Similar to CSound's phaser1 by Sean Costello"); + names.emplace_back("1"); + names.emplace_back(""); - for (const auto& name : names) { - assert(!Symbol::symbolify(name).empty()); - } + for (const auto& name : names) { + assert(!Symbol::symbolify(name).empty()); + } - const Symbol original("sym"); - assert(original == original); + const Symbol original("sym"); + assert(original == original); - bool valid = true; - try { - Symbol symbol("0startswithdigit"); - } catch (const Symbol::BadSymbol& e) { - std::cerr << "Caught exception: " << e.what() << std::endl; - valid = false; - } - assert(!valid); + bool valid = true; + try { + Symbol symbol("0startswithdigit"); + } catch (const Symbol::BadSymbol& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + assert(!valid); - valid = true; - try { - Symbol symbol(std::string("invalid/symbol")); - } catch (const Symbol::BadSymbol& e) { - std::cerr << "Caught exception: " << e.what() << std::endl; - valid = false; - } - assert(!valid); + valid = true; + try { + Symbol symbol(std::string("invalid/symbol")); + } catch (const Symbol::BadSymbol& e) { + std::cerr << "Caught exception: " << e.what() << std::endl; + valid = false; + } + assert(!valid); - return 0; + return 0; } diff --git a/test/thread_test.cpp b/test/thread_test.cpp index b2a8144..8c2d13a 100644 --- a/test/thread_test.cpp +++ b/test/thread_test.cpp @@ -32,12 +32,12 @@ std::atomic<int> n_errors(0); void wait_for_sem(Semaphore* sem) { - var = 41; - std::cout << "[Waiter] Waiting for signal..." << std::endl; - sem->wait(); - std::cout << "[Waiter] Received signal, exiting" << std::endl; - var = 42; - assert(var == 42); + var = 41; + std::cout << "[Waiter] Waiting for signal..." << std::endl; + sem->wait(); + std::cout << "[Waiter] Received signal, exiting" << std::endl; + var = 42; + assert(var == 42); } } // namespace @@ -45,20 +45,20 @@ wait_for_sem(Semaphore* sem) int main() { - Semaphore sem(0); - std::thread waiter(wait_for_sem, &sem); + Semaphore sem(0); + std::thread waiter(wait_for_sem, &sem); - var = 24; + var = 24; - std::cout << "[Main] Signalling..." << std::endl; - sem.post(); + std::cout << "[Main] Signalling..." << std::endl; + sem.post(); - std::cout << "[Main] Waiting for waiter..." << std::endl; - waiter.join(); + std::cout << "[Main] Waiting for waiter..." << std::endl; + waiter.join(); - std::cout << "[Main] Exiting" << std::endl; + std::cout << "[Main] Exiting" << std::endl; - assert(var == 24); + assert(var == 24); - return n_errors.load(); + return n_errors.load(); } diff --git a/test/time_test.cpp b/test/time_test.cpp index 6bd1e38..1746a7e 100644 --- a/test/time_test.cpp +++ b/test/time_test.cpp @@ -13,8 +13,8 @@ along with Raul. If not, see <http://www.gnu.org/licenses/>. */ -#include "raul/TimeStamp.hpp" #include "raul/TimeSlice.hpp" +#include "raul/TimeStamp.hpp" #include <cstdint> #include <iostream> @@ -22,24 +22,24 @@ int main() { - Raul::TimeUnit unit(Raul::TimeUnit::BEATS, 19200); - Raul::TimeSlice ts(48000, 19200, 120.0); + Raul::TimeUnit unit(Raul::TimeUnit::BEATS, 19200); + Raul::TimeSlice ts(48000, 19200, 120.0); - double in_double = 2.5; + double in_double = 2.5; - Raul::TimeStamp t(unit, - static_cast<uint32_t>(in_double), - static_cast<uint32_t>( - (in_double - static_cast<uint32_t>(in_double)) * - unit.ppt())); + Raul::TimeStamp t( + unit, + static_cast<uint32_t>(in_double), + static_cast<uint32_t>((in_double - static_cast<uint32_t>(in_double)) * + unit.ppt())); - std::cout << "\tSeconds: "; - std::cout << ts.beats_to_seconds(t); - std::cout << std::endl; + std::cout << "\tSeconds: "; + std::cout << ts.beats_to_seconds(t); + std::cout << std::endl; - std::cout << "\tTicks: "; - std::cout << ts.beats_to_ticks(t); - std::cout << std::endl; + std::cout << "\tTicks: "; + std::cout << ts.beats_to_ticks(t); + std::cout << std::endl; - return 0; + return 0; } |