diff --git a/distrho/extra/RingBuffer.hpp b/distrho/extra/RingBuffer.hpp index 71f1018a..be744dd1 100644 --- a/distrho/extra/RingBuffer.hpp +++ b/distrho/extra/RingBuffer.hpp @@ -404,6 +404,36 @@ public: return false; } + // ------------------------------------------------------------------- + // peek operations (returns a value without advancing read position) + + /* + * Peek for an unsigned 32-bit integer. + * Returns 0 if reading fails. + */ + uint32_t peekUInt() const noexcept + { + uint32_t ui = 0; + return tryPeek(&ui, sizeof(int32_t)) ? ui : 0; + } + + /*! + * Peek for a custom data type specified by the template typename used, + * with size being automatically deduced by the compiler (through the use of sizeof). + * + * Returns true if peeking succeeds. + * In case of failure, @a type value is automatically cleared by its deduced size. + */ + template + bool peekCustomType(T& type) const noexcept + { + if (tryPeek(&type, sizeof(T))) + return true; + + std::memset(&type, 0, sizeof(T)); + return false; + } + // ------------------------------------------------------------------- // write operations @@ -601,7 +631,7 @@ protected: } else { - const uint32_t firstpart(buffer->size - tail); + const uint32_t firstpart = buffer->size - tail; std::memcpy(bytebuf, buffer->buf + tail, firstpart); std::memcpy(bytebuf + firstpart, buffer->buf, readto); } @@ -619,6 +649,63 @@ protected: return true; } + /** @internal try reading from the buffer, can fail. */ + bool tryPeek(void* const buf, const uint32_t size) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false); + #if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wtautological-pointer-compare" + #endif + DISTRHO_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false); + #if defined(__clang__) + #pragma clang diagnostic pop + #endif + DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false); + DISTRHO_SAFE_ASSERT_RETURN(size > 0, false); + DISTRHO_SAFE_ASSERT_RETURN(size < buffer->size, false); + + // empty + if (buffer->head == buffer->tail) + return false; + + uint8_t* const bytebuf = static_cast(buf); + + const uint32_t head = buffer->head; + const uint32_t tail = buffer->tail; + const uint32_t wrap = head > tail ? 0 : buffer->size; + + if (size > wrap + head - tail) + return false; + + uint32_t readto = tail + size; + + if (readto > buffer->size) + { + readto -= buffer->size; + + if (size == 1) + { + std::memcpy(bytebuf, buffer->buf + tail, 1); + } + else + { + const uint32_t firstpart = buffer->size - tail; + std::memcpy(bytebuf, buffer->buf + tail, firstpart); + std::memcpy(bytebuf + firstpart, buffer->buf, readto); + } + } + else + { + std::memcpy(bytebuf, buffer->buf + tail, size); + + if (readto == buffer->size) + readto = 0; + } + + return true; + } + /** @internal try writing to the buffer, can fail. */ bool tryWrite(const void* const buf, const uint32_t size) noexcept { @@ -656,7 +743,7 @@ protected: } else { - const uint32_t firstpart(buffer->size - wrtn); + const uint32_t firstpart = buffer->size - wrtn; std::memcpy(buffer->buf + wrtn, bytebuf, firstpart); std::memcpy(buffer->buf, bytebuf + firstpart, writeto); }