From 23a3508d819aff48671a53d04e3fe24ca24bb731 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 28 Mar 2020 14:34:55 +0100 Subject: [PATCH] Add the spsc ring buffer --- vst/CMakeLists.txt | 4 + vst/external/ring_buffer/LICENSE | 23 ++++ .../ring_buffer/ring_buffer/ring_buffer.cpp | 105 ++++++++++++++++++ .../ring_buffer/ring_buffer/ring_buffer.h | 69 ++++++++++++ .../ring_buffer/ring_buffer/ring_buffer.tcc | 61 ++++++++++ 5 files changed, 262 insertions(+) create mode 100644 vst/external/ring_buffer/LICENSE create mode 100644 vst/external/ring_buffer/ring_buffer/ring_buffer.cpp create mode 100644 vst/external/ring_buffer/ring_buffer/ring_buffer.h create mode 100644 vst/external/ring_buffer/ring_buffer/ring_buffer.tcc diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index 92228d45..377b7dd0 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -60,6 +60,10 @@ set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES plugin_add_vst3sdk(${VSTPLUGIN_PRJ_NAME}) plugin_add_vstgui(${VSTPLUGIN_PRJ_NAME}) +# Add the ring buffer +target_include_directories(${VSTPLUGIN_PRJ_NAME} PRIVATE "external/ring_buffer") +target_sources(${VSTPLUGIN_PRJ_NAME} PRIVATE "external/ring_buffer/ring_buffer/ring_buffer.cpp") + if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") target_link_libraries(${VSTPLUGIN_PRJ_NAME} PRIVATE "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/vst3.version") diff --git a/vst/external/ring_buffer/LICENSE b/vst/external/ring_buffer/LICENSE new file mode 100644 index 00000000..36b7cd93 --- /dev/null +++ b/vst/external/ring_buffer/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp b/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp new file mode 100644 index 00000000..bf0fcb9c --- /dev/null +++ b/vst/external/ring_buffer/ring_buffer/ring_buffer.cpp @@ -0,0 +1,105 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "ring_buffer.h" +#include +#include + +#if defined(__cpp_if_constexpr) +# define if_constexpr if constexpr +#else +# define if_constexpr if +#endif + +template +Ring_Buffer_Ex::Ring_Buffer_Ex(size_t capacity) + : cap_(capacity + 1), + rbdata_(new uint8_t[capacity + 1]) +{ +} + +template +Ring_Buffer_Ex::~Ring_Buffer_Ex() +{ +} + +template +size_t Ring_Buffer_Ex::size_used() const +{ + const size_t rp = rp_, wp = wp_, cap = cap_; + return wp + ((wp < rp) ? cap : 0) - rp; +} + +template +bool Ring_Buffer_Ex::discard(size_t len) +{ + return getbytes_ex_(nullptr, len, true); +} + +template +size_t Ring_Buffer_Ex::size_free() const +{ + const size_t rp = rp_, wp = wp_, cap = cap_; + return rp + ((rp <= wp) ? cap : 0) - wp - 1; +} + +template +bool Ring_Buffer_Ex::getbytes_(void *data, size_t len) +{ + return getbytes_ex_(data, len, true); +} + +template +bool Ring_Buffer_Ex::peekbytes_(void *data, size_t len) const +{ + auto *ncthis = const_cast *>(this); + return ncthis->getbytes_ex_(data, len, false); +} + +template +bool Ring_Buffer_Ex::getbytes_ex_(void *data, size_t len, bool advp) +{ + if (size_used() < len) + return false; + + const size_t rp = rp_, cap = cap_; + const uint8_t *src = rbdata_.get(); + uint8_t *dst = (uint8_t *)data; + + if (data) { + const size_t taillen = std::min(len, cap - rp); + if_constexpr (Atomic) + std::atomic_thread_fence(std::memory_order_acquire); + std::copy_n(&src[rp], taillen, dst); + std::copy_n(src, len - taillen, dst + taillen); + } + + if (advp) + rp_ = (rp + len < cap) ? (rp + len) : (rp + len - cap); + return true; +} + +template +bool Ring_Buffer_Ex::putbytes_(const void *data, size_t len) +{ + if (size_free() < len) + return false; + + const size_t wp = wp_, cap = cap_; + const uint8_t *src = (const uint8_t *)data; + uint8_t *dst = rbdata_.get(); + + const size_t taillen = std::min(len, cap - wp); + std::copy_n(src, taillen, &dst[wp]); + std::copy_n(src + taillen, len - taillen, dst); + if_constexpr (Atomic) + std::atomic_thread_fence(std::memory_order_release); + + wp_ = (wp + len < cap) ? (wp + len) : (wp + len - cap); + return true; +} + +template class Ring_Buffer_Ex; +template class Ring_Buffer_Ex; diff --git a/vst/external/ring_buffer/ring_buffer/ring_buffer.h b/vst/external/ring_buffer/ring_buffer/ring_buffer.h new file mode 100644 index 00000000..395ba9e0 --- /dev/null +++ b/vst/external/ring_buffer/ring_buffer/ring_buffer.h @@ -0,0 +1,69 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#pragma once +#include +#include +#include +#include +#include + +#if defined(__cpp_lib_atomic_is_always_lock_free) +static_assert( + std::atomic::is_always_lock_free, "atomic must be lock free"); +#endif + +//------------------------------------------------------------------------------ +template class Ring_Buffer_Ex; +typedef Ring_Buffer_Ex Ring_Buffer; + +//------------------------------------------------------------------------------ +template +class Basic_Ring_Buffer { +public: + // read operations + template bool get(T &x); + template bool get(T *x, size_t n); + template bool peek(T &x); + template bool peek(T *x, size_t n); + // write operations + template bool put(const T &x); + template bool put(const T *x, size_t n); +}; + +//------------------------------------------------------------------------------ +template +class Ring_Buffer_Ex final : + private Basic_Ring_Buffer> { +private: + typedef Basic_Ring_Buffer> Base; +public: + // initialization and cleanup + explicit Ring_Buffer_Ex(size_t capacity); + ~Ring_Buffer_Ex(); + // attributes + size_t capacity() const; + // read operations + size_t size_used() const; + bool discard(size_t len); + using Base::get; + using Base::peek; + // write operations + size_t size_free() const; + using Base::put; + +private: + size_t cap_{0}; + typename std::conditional, size_t>::type rp_{0}, wp_{0}; + std::unique_ptr rbdata_ {}; + friend Base; + bool getbytes_(void *data, size_t len); + bool peekbytes_(void *data, size_t len) const; + bool getbytes_ex_(void *data, size_t len, bool advp); + bool putbytes_(const void *data, size_t len); +}; + +//------------------------------------------------------------------------------ +#include "ring_buffer.tcc" diff --git a/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc b/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc new file mode 100644 index 00000000..b53b17f4 --- /dev/null +++ b/vst/external/ring_buffer/ring_buffer/ring_buffer.tcc @@ -0,0 +1,61 @@ +// Copyright Jean Pierre Cimalando 2018. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "ring_buffer.h" + +template +inline size_t Ring_Buffer_Ex::capacity() const +{ + return cap_ - 1; +} + +//------------------------------------------------------------------------------ +template +template +inline bool Basic_Ring_Buffer::get(T &x) +{ + return get(&x, 1); +} + +template +template +inline bool Basic_Ring_Buffer::get(T *x, size_t n) +{ + // static_assert(std::is_trivially_copyable::value, "ring_buffer: T must be trivially copyable"); + RB *self = static_cast(this); + return self->getbytes_(x, n * sizeof(T)); +} + +template +template +inline bool Basic_Ring_Buffer::peek(T &x) +{ + return peek(&x, 1); +} + +template +template +inline bool Basic_Ring_Buffer::peek(T *x, size_t n) +{ + // static_assert(std::is_trivially_copyable::value, "ring_buffer: T must be trivially copyable"); + RB *self = static_cast(this); + return self->peekbytes_(x, n * sizeof(T)); +} + +template +template +inline bool Basic_Ring_Buffer::put(const T &x) +{ + return put(&x, 1); +} + +template +template +inline bool Basic_Ring_Buffer::put(const T *x, size_t n) +{ + // static_assert(std::is_trivially_copyable::value, "ring_buffer: T must be trivially copyable"); + RB *self = static_cast(this); + return self->putbytes_(x, n * sizeof(T)); +}