mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-24 07:31:48 +00:00
334 lines
8.4 KiB
C++
334 lines
8.4 KiB
C++
// Copyright 2022, Collabora, Ltd.
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
/*!
|
|
* @file
|
|
* @brief Wrap some ring buffer internals for somewhat generic C usage.
|
|
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
|
* @ingroup aux_util
|
|
*/
|
|
|
|
#include "u_id_ringbuffer.h"
|
|
#include "u_iterator_base.hpp"
|
|
#include "u_template_historybuf_impl_helpers.hpp"
|
|
#include "u_logging.h"
|
|
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <iterator>
|
|
#include <type_traits>
|
|
|
|
using xrt::auxiliary::util::RandomAccessIteratorBase;
|
|
using xrt::auxiliary::util::detail::RingBufferHelper;
|
|
using id_value_type = uint64_t;
|
|
struct u_id_ringbuffer
|
|
{
|
|
u_id_ringbuffer(uint32_t capacity_) : helper(capacity_), ids(capacity_, 0), capacity(capacity_) {}
|
|
RingBufferHelper helper;
|
|
std::vector<id_value_type> ids;
|
|
uint32_t capacity;
|
|
};
|
|
|
|
namespace {
|
|
|
|
// just enough iterator to get it done: basically copy and paste from u_template_historybuf_const_iterator.inl
|
|
struct IdRingbufferIterator : public RandomAccessIteratorBase<const RingBufferHelper>
|
|
{
|
|
using base = RandomAccessIteratorBase<const RingBufferHelper>;
|
|
using Self = IdRingbufferIterator;
|
|
using container_type = const u_id_ringbuffer;
|
|
using typename base::difference_type;
|
|
using typename base::iterator_category;
|
|
using value_type = id_value_type;
|
|
using pointer = const value_type *;
|
|
using reference = const value_type;
|
|
|
|
container_type *container_{nullptr};
|
|
|
|
IdRingbufferIterator(container_type *container, base &&iter_base)
|
|
: base(std::move(iter_base)), container_(container)
|
|
{}
|
|
|
|
static Self
|
|
begin(container_type &container)
|
|
{
|
|
return {&container, base::begin(container.helper)};
|
|
}
|
|
|
|
static Self
|
|
end(container_type &container)
|
|
{
|
|
return {&container, base::end(container.helper)};
|
|
}
|
|
|
|
//! returns negative if invalid
|
|
int32_t
|
|
inner_index() const
|
|
{
|
|
if (!base::valid()) {
|
|
return -1;
|
|
}
|
|
size_t inner_index = 0;
|
|
if (!container_->helper.index_to_inner_index(base::index(), inner_index)) {
|
|
return -1;
|
|
}
|
|
return static_cast<int32_t>(inner_index);
|
|
}
|
|
|
|
//! Dereference operator: throws std::out_of_range if invalid
|
|
uint64_t
|
|
operator*() const
|
|
{
|
|
if (!base::valid()) {
|
|
throw std::out_of_range("Iterator not valid");
|
|
}
|
|
size_t inner_index = 0;
|
|
if (!container_->helper.index_to_inner_index(base::index(), inner_index)) {
|
|
throw std::out_of_range("Iterator not valid");
|
|
}
|
|
return container_->ids[inner_index];
|
|
}
|
|
|
|
|
|
//! Pre-increment: Advance, then return self.
|
|
Self &
|
|
operator++()
|
|
{
|
|
this->increment_n(1);
|
|
return *this;
|
|
}
|
|
|
|
//! Pre-decrement: Subtract, then return self.
|
|
Self &
|
|
operator--()
|
|
{
|
|
this->decrement_n(1);
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
static_assert(std::is_same<typename std::iterator_traits<IdRingbufferIterator>::iterator_category,
|
|
std::random_access_iterator_tag>::value,
|
|
"Iterator should be random access");
|
|
} // namespace
|
|
|
|
#define DEFAULT_CATCH(RETURNVAL) \
|
|
catch (std::exception const &e) \
|
|
{ \
|
|
U_LOG_E("Caught exception: %s", e.what()); \
|
|
return RETURNVAL; \
|
|
} \
|
|
catch (...) \
|
|
{ \
|
|
U_LOG_E("Caught exception"); \
|
|
return RETURNVAL; \
|
|
}
|
|
|
|
struct u_id_ringbuffer *
|
|
u_id_ringbuffer_create(uint32_t capacity)
|
|
{
|
|
try {
|
|
auto ret = std::make_unique<u_id_ringbuffer>(capacity);
|
|
return ret.release();
|
|
}
|
|
DEFAULT_CATCH(nullptr)
|
|
}
|
|
|
|
// Common wrapping to catch exceptions in functions that return an index or negative for error.
|
|
template <typename F>
|
|
static inline int64_t
|
|
exceptionCatchingWrapper(F &&func)
|
|
{
|
|
try {
|
|
return func();
|
|
} catch (std::exception const &e) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
int64_t
|
|
u_id_ringbuffer_push_back(struct u_id_ringbuffer *uirb, uint64_t id)
|
|
{
|
|
try {
|
|
auto inner_index = uirb->helper.push_back_location();
|
|
uirb->ids[inner_index] = id;
|
|
return static_cast<int32_t>(inner_index);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
void
|
|
u_id_ringbuffer_pop_front(struct u_id_ringbuffer *uirb)
|
|
{
|
|
try {
|
|
uirb->helper.pop_front();
|
|
}
|
|
DEFAULT_CATCH()
|
|
}
|
|
|
|
|
|
int32_t
|
|
u_id_ringbuffer_get_back(struct u_id_ringbuffer *uirb, uint64_t *out_id)
|
|
{
|
|
try {
|
|
auto inner_index = uirb->helper.back_inner_index();
|
|
if (inner_index == uirb->capacity) {
|
|
return -1;
|
|
}
|
|
*out_id = uirb->ids[inner_index];
|
|
return static_cast<int32_t>(inner_index);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
int32_t
|
|
u_id_ringbuffer_get_front(struct u_id_ringbuffer *uirb, uint64_t *out_id)
|
|
{
|
|
|
|
try {
|
|
auto inner_index = uirb->helper.front_inner_index();
|
|
if (inner_index == uirb->capacity) {
|
|
return -1;
|
|
}
|
|
*out_id = uirb->ids[inner_index];
|
|
return static_cast<int32_t>(inner_index);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
uint32_t
|
|
u_id_ringbuffer_get_size(struct u_id_ringbuffer const *uirb)
|
|
{
|
|
try {
|
|
return static_cast<int32_t>(uirb->helper.size());
|
|
}
|
|
DEFAULT_CATCH(0)
|
|
}
|
|
|
|
bool
|
|
u_id_ringbuffer_is_empty(struct u_id_ringbuffer const *uirb)
|
|
{
|
|
try {
|
|
return uirb->helper.empty();
|
|
}
|
|
DEFAULT_CATCH(true)
|
|
}
|
|
|
|
// Handle conditional setting of value pointed to by out_id, and casting of inner_index
|
|
static inline int32_t
|
|
handleGetterResult(struct u_id_ringbuffer const *uirb, size_t inner_index, uint64_t *out_id)
|
|
{
|
|
if (out_id != nullptr) {
|
|
*out_id = uirb->ids[inner_index];
|
|
}
|
|
return static_cast<int32_t>(inner_index);
|
|
}
|
|
|
|
int32_t
|
|
u_id_ringbuffer_get_at_age(struct u_id_ringbuffer *uirb, uint32_t age, uint64_t *out_id)
|
|
{
|
|
try {
|
|
size_t inner_index = 0;
|
|
if (!uirb->helper.age_to_inner_index(age, inner_index)) {
|
|
// out of range
|
|
return -1;
|
|
}
|
|
return handleGetterResult(uirb, inner_index, out_id);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
int32_t
|
|
u_id_ringbuffer_get_at_clamped_age(struct u_id_ringbuffer *uirb, uint32_t age, uint64_t *out_id)
|
|
{
|
|
try {
|
|
size_t inner_index = 0;
|
|
if (!uirb->helper.clamped_age_to_inner_index(age, inner_index)) {
|
|
// out of range
|
|
return -1;
|
|
}
|
|
return handleGetterResult(uirb, inner_index, out_id);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
int32_t
|
|
u_id_ringbuffer_get_at_index(struct u_id_ringbuffer *uirb, uint32_t index, uint64_t *out_id)
|
|
{
|
|
try {
|
|
size_t inner_index = 0;
|
|
if (!uirb->helper.index_to_inner_index(index, inner_index)) {
|
|
// out of range
|
|
return -1;
|
|
}
|
|
return handleGetterResult(uirb, inner_index, out_id);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
// Handle validity of iterators, conditional setting of values pointed to by out_id and out_index, and return value
|
|
static inline int32_t
|
|
handleAlgorithmResult(IdRingbufferIterator it, uint64_t *out_id, uint32_t *out_index)
|
|
{
|
|
if (!it.valid()) {
|
|
return -1;
|
|
}
|
|
if (out_id != nullptr) {
|
|
*out_id = *it;
|
|
}
|
|
if (out_index != nullptr) {
|
|
*out_index = it.index();
|
|
}
|
|
return it.inner_index();
|
|
}
|
|
|
|
int32_t
|
|
u_id_ringbuffer_lower_bound_id(struct u_id_ringbuffer *uirb, uint64_t search_id, uint64_t *out_id, uint32_t *out_index)
|
|
{
|
|
try {
|
|
|
|
const auto b = IdRingbufferIterator::begin(*uirb);
|
|
const auto e = IdRingbufferIterator::end(*uirb);
|
|
|
|
// find the first element *not less than* our ID: binary search
|
|
const auto it = std::lower_bound(b, e, search_id);
|
|
return handleAlgorithmResult(it, out_id, out_index);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
int32_t
|
|
u_id_ringbuffer_find_id_unordered(struct u_id_ringbuffer *uirb,
|
|
uint64_t search_id,
|
|
uint64_t *out_id,
|
|
uint32_t *out_index)
|
|
{
|
|
try {
|
|
|
|
const auto b = IdRingbufferIterator::begin(*uirb);
|
|
const auto e = IdRingbufferIterator::end(*uirb);
|
|
|
|
// find the matching ID with simple linear search
|
|
const auto it = std::find(b, e, search_id);
|
|
return handleAlgorithmResult(it, out_id, out_index);
|
|
}
|
|
DEFAULT_CATCH(-1)
|
|
}
|
|
|
|
void
|
|
u_id_ringbuffer_destroy(struct u_id_ringbuffer **ptr_to_uirb)
|
|
{
|
|
try {
|
|
if (ptr_to_uirb == nullptr) {
|
|
return;
|
|
}
|
|
struct u_id_ringbuffer *uirb = *ptr_to_uirb;
|
|
if (uirb == nullptr) {
|
|
return;
|
|
}
|
|
delete uirb;
|
|
*ptr_to_uirb = nullptr;
|
|
}
|
|
DEFAULT_CATCH()
|
|
}
|