mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2024-12-28 18:46:18 +00:00
a/util: Wrap "just enough" of the ring buffer stuff for generic use from C.
This commit is contained in:
parent
3b0252bda8
commit
a904914e6e
|
@ -171,6 +171,8 @@ add_library(
|
|||
util/u_hashmap.h
|
||||
util/u_hashset.cpp
|
||||
util/u_hashset.h
|
||||
util/u_id_ringbuffer.cpp
|
||||
util/u_id_ringbuffer.h
|
||||
util/u_json.c
|
||||
util/u_json.h
|
||||
util/u_json.hpp
|
||||
|
|
|
@ -50,6 +50,8 @@ lib_aux_util = static_library(
|
|||
'util/u_hashmap.h',
|
||||
'util/u_hashset.cpp',
|
||||
'util/u_hashset.h',
|
||||
'util/u_id_ringbuffer.cpp',
|
||||
'util/u_id_ringbuffer.h',
|
||||
'util/u_json.c',
|
||||
'util/u_json.h',
|
||||
'util/u_json.hpp',
|
||||
|
|
333
src/xrt/auxiliary/util/u_id_ringbuffer.cpp
Normal file
333
src/xrt/auxiliary/util/u_id_ringbuffer.cpp
Normal file
|
@ -0,0 +1,333 @@
|
|||
// 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()
|
||||
}
|
211
src/xrt/auxiliary/util/u_id_ringbuffer.h
Normal file
211
src/xrt/auxiliary/util/u_id_ringbuffer.h
Normal file
|
@ -0,0 +1,211 @@
|
|||
// Copyright 2022, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Ring buffer for things keyed on an ID but otherwise maintained externally, for C usage.
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
* @ingroup aux_util
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Container type to let you store IDs in a ring buffer, and maybe your own data in your own parallel array.
|
||||
*
|
||||
* The IDs are just uint64_t. If you don't need any of the order-dependent functionality, you can treat use them for any
|
||||
* purpose you like.
|
||||
*
|
||||
* Some functionality requires that IDs be pushed in increasing order, but it's highlighted in the docs.
|
||||
* If you need more than this, either extend this or use the underlying C++ container if that's an OK solution for you.
|
||||
*
|
||||
*/
|
||||
struct u_id_ringbuffer;
|
||||
|
||||
/**
|
||||
* Create a ringbuffer for storing IDs.
|
||||
*
|
||||
* You might keep an array of equivalent capacity locally: methods of this container will tell you which index in that
|
||||
* array to interact with.
|
||||
*
|
||||
* @param capacity
|
||||
* @return struct u_id_ringbuffer*
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
struct u_id_ringbuffer *
|
||||
u_id_ringbuffer_create(uint32_t capacity);
|
||||
|
||||
/**
|
||||
* Push a new element to the back
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param id The ID to push back. It is your responsibility to make sure you insert in order if you want to use ordered
|
||||
* methods.
|
||||
* @return the "inner" index in your array to store any associated data, or negative if error.
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
int64_t
|
||||
u_id_ringbuffer_push_back(struct u_id_ringbuffer *uirb, uint64_t id);
|
||||
|
||||
/**
|
||||
* Pop an element from the front, if any
|
||||
*
|
||||
* @param uirb self pointer.
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
void
|
||||
u_id_ringbuffer_pop_front(struct u_id_ringbuffer *uirb);
|
||||
|
||||
/**
|
||||
* Get the back (most recent) of the buffer
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param[out] out_id Where to store the back ID
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
int32_t
|
||||
u_id_ringbuffer_get_back(struct u_id_ringbuffer *uirb, uint64_t *out_id);
|
||||
|
||||
/**
|
||||
* Get the front (least recent) of the buffer
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param[out] out_id Where to store the front ID
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
int32_t
|
||||
u_id_ringbuffer_get_front(struct u_id_ringbuffer *uirb, uint64_t *out_id);
|
||||
|
||||
/**
|
||||
* Get the number of elements in the buffer
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @return the size
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
uint32_t
|
||||
u_id_ringbuffer_get_size(struct u_id_ringbuffer const *uirb);
|
||||
|
||||
/**
|
||||
* Get whether the buffer is empty
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @return true if empty
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
bool
|
||||
u_id_ringbuffer_is_empty(struct u_id_ringbuffer const *uirb);
|
||||
|
||||
/**
|
||||
* Get an element a certain distance ("age") from the back of the buffer
|
||||
*
|
||||
* See u_id_ringbuffer_get_at_clamped_age() if you want to clamp the age
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param age the distance from the back (0 is the same as back, 1 is previous, etc)
|
||||
* @param[out] out_id Where to store the ID, if successful (optional)
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, out of range, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
int32_t
|
||||
u_id_ringbuffer_get_at_age(struct u_id_ringbuffer *uirb, uint32_t age, uint64_t *out_id);
|
||||
|
||||
/**
|
||||
* Get an element a certain distance ("age") from the back of the buffer, clamping age to stay in bounds as long as the
|
||||
* buffer is not empty.
|
||||
*
|
||||
* See u_id_ringbuffer_get_at_age() if you don't want clamping.
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param age the distance from the back (0 is the same as back, 1 is previous, etc)
|
||||
* @param[out] out_id Where to store the ID, if successful (optional)
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
int32_t
|
||||
u_id_ringbuffer_get_at_clamped_age(struct u_id_ringbuffer *uirb, uint32_t age, uint64_t *out_id);
|
||||
|
||||
/**
|
||||
* Get an element a certain index from the front of the (logical) buffer
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param index the distance from the front (0 is the same as front, 1 is newer, etc)
|
||||
* @param[out] out_id Where to store the ID, if successful (optional)
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, out of range, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
int32_t
|
||||
u_id_ringbuffer_get_at_index(struct u_id_ringbuffer *uirb, uint32_t index, uint64_t *out_id);
|
||||
|
||||
/**
|
||||
* Find the latest element not less than the supplied ID @p search_id .
|
||||
*
|
||||
* Assumes/depends on your maintenance of entries in ascending order. If you aren't ensuring this, use
|
||||
* u_id_ringbuffer_find_id_unordered() instead.
|
||||
*
|
||||
* (Wraps `std::lower_bound`)
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param search_id the ID to search for.
|
||||
* @param[out] out_id Where to store the ID found, if successful (optional)
|
||||
* @param[out] out_index Where to store the ring buffer index (not the inner index) found, if successful (optional)
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Find the element with the supplied ID @p search_id in an unordered buffer.
|
||||
*
|
||||
* This does *not* depend on order so just does a linear search. If you are keeping your IDs in ascending order, use
|
||||
* u_id_ringbuffer_lower_bound_id() instead.
|
||||
*
|
||||
* @param uirb self pointer
|
||||
* @param search_id the ID to search for.
|
||||
* @param[out] out_id Where to store the ID found, if successful (optional)
|
||||
* @param[out] out_index Where to store the ring buffer index (not the inner index) found, if successful (optional)
|
||||
* @return the "inner" index in your array with any associated data, or negative if error (empty, etc).
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Destroy an ID ring buffer.
|
||||
*
|
||||
* Does null checks.
|
||||
*
|
||||
* @param ptr_to_uirb Address of your ring buffer pointer. Will be set to zero.
|
||||
*
|
||||
* @public @memberof u_id_ringbuffer
|
||||
*/
|
||||
void
|
||||
u_id_ringbuffer_destroy(struct u_id_ringbuffer **ptr_to_uirb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
|
@ -35,6 +35,12 @@ target_link_libraries(tests_history_buf PRIVATE tests_main)
|
|||
target_link_libraries(tests_history_buf PRIVATE aux_util aux_math)
|
||||
add_test(NAME tests_history_buf COMMAND tests_history_buf --success)
|
||||
|
||||
# id ring buffer (closely related to history buf)
|
||||
add_executable(tests_id_ringbuffer tests_id_ringbuffer.cpp)
|
||||
target_link_libraries(tests_id_ringbuffer PRIVATE tests_main)
|
||||
target_link_libraries(tests_id_ringbuffer PRIVATE aux_util)
|
||||
add_test(NAME tests_id_ringbuffer COMMAND tests_id_ringbuffer --success)
|
||||
|
||||
# pacing
|
||||
add_executable(tests_pacing tests_pacing.cpp)
|
||||
target_link_libraries(tests_pacing PRIVATE tests_main)
|
||||
|
|
199
tests/tests_id_ringbuffer.cpp
Normal file
199
tests/tests_id_ringbuffer.cpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2021-2022, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief u_id_ringbuffer collection tests.
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
*/
|
||||
|
||||
#include <util/u_id_ringbuffer.h>
|
||||
|
||||
|
||||
|
||||
#include "catch/catch.hpp"
|
||||
|
||||
|
||||
TEST_CASE("u_template_historybuf")
|
||||
{
|
||||
auto buffer = u_id_ringbuffer_create(4);
|
||||
SECTION("behavior when empty")
|
||||
{
|
||||
CHECK(u_id_ringbuffer_is_empty(buffer));
|
||||
CHECK(0 == u_id_ringbuffer_get_size(buffer));
|
||||
uint64_t out_id = 0;
|
||||
CHECK(u_id_ringbuffer_get_front(buffer, &out_id) < 0);
|
||||
CHECK(u_id_ringbuffer_get_back(buffer, &out_id) < 0);
|
||||
}
|
||||
SECTION("behavior with one")
|
||||
{
|
||||
int32_t zero_inner_index;
|
||||
REQUIRE((zero_inner_index = u_id_ringbuffer_push_back(buffer, 0)) >= 0);
|
||||
CHECK_FALSE(u_id_ringbuffer_is_empty(buffer));
|
||||
CHECK(1 == u_id_ringbuffer_get_size(buffer));
|
||||
CAPTURE(zero_inner_index);
|
||||
|
||||
|
||||
// check front/back
|
||||
uint64_t out_id_front = 55;
|
||||
uint64_t out_id_back = 55;
|
||||
CHECK(u_id_ringbuffer_get_front(buffer, &out_id_front) >= 0);
|
||||
CHECK(u_id_ringbuffer_get_front(buffer, &out_id_front) == zero_inner_index);
|
||||
CHECK(out_id_front == 0);
|
||||
CHECK(u_id_ringbuffer_get_back(buffer, &out_id_back) >= 0);
|
||||
CHECK(u_id_ringbuffer_get_back(buffer, &out_id_front) == zero_inner_index);
|
||||
CHECK(out_id_back == 0);
|
||||
CHECK(out_id_front == out_id_back);
|
||||
|
||||
// check contents
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_index(buffer, 0, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_age(buffer, 0, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 0, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_age(buffer, 1, &out_id) < 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 1, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 2, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("behavior with two")
|
||||
{
|
||||
int32_t zero_inner_index;
|
||||
int32_t one_inner_index;
|
||||
REQUIRE((zero_inner_index = u_id_ringbuffer_push_back(buffer, 0)) >= 0);
|
||||
REQUIRE((one_inner_index = u_id_ringbuffer_push_back(buffer, 1)) >= 0);
|
||||
REQUIRE(zero_inner_index != one_inner_index);
|
||||
CAPTURE(zero_inner_index);
|
||||
CAPTURE(one_inner_index);
|
||||
CHECK_FALSE(u_id_ringbuffer_is_empty(buffer));
|
||||
CHECK(2 == u_id_ringbuffer_get_size(buffer));
|
||||
|
||||
SECTION("check front and back")
|
||||
{
|
||||
// check front/back
|
||||
uint64_t out_id_front = 55;
|
||||
uint64_t out_id_back = 55;
|
||||
CHECK(u_id_ringbuffer_get_front(buffer, &out_id_front) == zero_inner_index);
|
||||
CHECK(out_id_front == 0);
|
||||
CHECK(u_id_ringbuffer_get_back(buffer, &out_id_back) == one_inner_index);
|
||||
CHECK(out_id_back == 1);
|
||||
CHECK(out_id_front != out_id_back);
|
||||
}
|
||||
SECTION("check contents")
|
||||
{
|
||||
// check contents
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_index(buffer, 0, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_index(buffer, 1, &out_id) == one_inner_index);
|
||||
CHECK(out_id == 1);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_index(buffer, 2, &out_id) < 0);
|
||||
}
|
||||
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_age(buffer, 0, &out_id) == one_inner_index);
|
||||
CHECK(out_id == 1);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 0, &out_id) == one_inner_index);
|
||||
CHECK(out_id == 1);
|
||||
}
|
||||
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_age(buffer, 1, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 1, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_age(buffer, 2, &out_id) < 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 2, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
{
|
||||
uint64_t out_id = 55;
|
||||
CHECK(u_id_ringbuffer_get_at_clamped_age(buffer, 3, &out_id) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("algorithm behavior with 3")
|
||||
{
|
||||
int32_t zero_inner_index;
|
||||
int32_t two_inner_index;
|
||||
int32_t four_inner_index;
|
||||
REQUIRE((zero_inner_index = u_id_ringbuffer_push_back(buffer, 0)) >= 0);
|
||||
REQUIRE((two_inner_index = u_id_ringbuffer_push_back(buffer, 2)) >= 0);
|
||||
REQUIRE((four_inner_index = u_id_ringbuffer_push_back(buffer, 4)) >= 0);
|
||||
CAPTURE(zero_inner_index);
|
||||
CAPTURE(two_inner_index);
|
||||
CAPTURE(four_inner_index);
|
||||
CHECK_FALSE(u_id_ringbuffer_is_empty(buffer));
|
||||
CHECK(3 == u_id_ringbuffer_get_size(buffer));
|
||||
|
||||
uint64_t out_id = 55;
|
||||
uint32_t out_index = 55;
|
||||
CHECK(u_id_ringbuffer_find_id_unordered(buffer, 0, nullptr, nullptr) == zero_inner_index);
|
||||
CHECK(u_id_ringbuffer_find_id_unordered(buffer, 0, &out_id, &out_index) == zero_inner_index);
|
||||
CHECK(out_id == 0);
|
||||
CHECK(out_index == 0);
|
||||
|
||||
CHECK(u_id_ringbuffer_find_id_unordered(buffer, 2, nullptr, nullptr) == two_inner_index);
|
||||
CHECK(u_id_ringbuffer_find_id_unordered(buffer, 2, &out_id, &out_index) == two_inner_index);
|
||||
CHECK(out_id == 2);
|
||||
CHECK(out_index == 1);
|
||||
|
||||
CHECK(u_id_ringbuffer_find_id_unordered(buffer, 4, nullptr, nullptr) == four_inner_index);
|
||||
CHECK(u_id_ringbuffer_find_id_unordered(buffer, 4, &out_id, &out_index) == four_inner_index);
|
||||
CHECK(out_id == 4);
|
||||
CHECK(out_index == 2);
|
||||
|
||||
// first id not less than 1 is id 2.
|
||||
out_id = 55;
|
||||
out_index = 55;
|
||||
CHECK(u_id_ringbuffer_lower_bound_id(buffer, 1, nullptr, nullptr) == two_inner_index);
|
||||
CHECK(u_id_ringbuffer_lower_bound_id(buffer, 1, &out_id, &out_index) == two_inner_index);
|
||||
CHECK(out_id == 2);
|
||||
CHECK(out_index == 1);
|
||||
}
|
||||
u_id_ringbuffer_destroy(&buffer);
|
||||
}
|
Loading…
Reference in a new issue