The open source OpenXR runtime
at main 341 lines 8.7 kB view raw
1// Copyright 2022, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Wrap some ring buffer internals for somewhat generic C usage. 6 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 7 * @ingroup aux_util 8 */ 9 10#include "u_id_ringbuffer.h" 11#include "u_iterator_base.hpp" 12#include "u_template_historybuf_impl_helpers.hpp" 13#include "u_logging.h" 14 15#include <vector> 16#include <memory> 17#include <iterator> 18#include <type_traits> 19 20 21using xrt::auxiliary::util::RandomAccessIteratorBase; 22using xrt::auxiliary::util::detail::RingBufferHelper; 23using id_value_type = uint64_t; 24struct u_id_ringbuffer 25{ 26 u_id_ringbuffer(uint32_t capacity_) : helper(capacity_), ids(capacity_, 0), capacity(capacity_) {} 27 RingBufferHelper helper; 28 std::vector<id_value_type> ids; 29 uint32_t capacity; 30}; 31 32namespace { 33 34// just-enough iterator to get it done: basically copy and paste from u_template_historybuf_const_iterator.inl 35struct IdRingbufferIterator : public RandomAccessIteratorBase<const RingBufferHelper> 36{ 37 using base = RandomAccessIteratorBase<const RingBufferHelper>; 38 using Self = IdRingbufferIterator; 39 using container_type = const u_id_ringbuffer; 40 using typename base::difference_type; 41 using typename base::iterator_category; 42 using value_type = id_value_type; 43 using pointer = const value_type *; 44 using reference = const value_type; 45 46 container_type *container_{nullptr}; 47 48 IdRingbufferIterator(container_type *container, base &&iter_base) : base(iter_base), container_(container) {} 49 50 static Self 51 begin(container_type &container) 52 { 53 return {&container, base::begin(container.helper)}; 54 } 55 56 static Self 57 end(container_type &container) 58 { 59 return {&container, base::end(container.helper)}; 60 } 61 62 //! returns negative if invalid 63 int32_t 64 inner_index() const 65 { 66 if (!base::valid()) { 67 return -1; 68 } 69 size_t inner_index = 0; 70 if (!container_->helper.index_to_inner_index(base::index(), inner_index)) { 71 return -1; 72 } 73 return static_cast<int32_t>(inner_index); 74 } 75 76 //! Dereference operator: throws std::out_of_range if invalid 77 uint64_t 78 operator*() const 79 { 80 if (!base::valid()) { 81 throw std::out_of_range("Iterator not valid"); 82 } 83 size_t inner_index = 0; 84 if (!container_->helper.index_to_inner_index(base::index(), inner_index)) { 85 throw std::out_of_range("Iterator not valid"); 86 } 87 return container_->ids[inner_index]; 88 } 89 90 91 //! Pre-increment: Advance, then return self. 92 Self & 93 operator++() 94 { 95 this->increment_n(1); 96 return *this; 97 } 98 99 //! Pre-decrement: Subtract, then return self. 100 Self & 101 operator--() 102 { 103 this->decrement_n(1); 104 return *this; 105 } 106}; 107 108static_assert(std::is_same<typename std::iterator_traits<IdRingbufferIterator>::iterator_category, 109 std::random_access_iterator_tag>::value, 110 "Iterator should be random access"); 111} // namespace 112 113#define DEFAULT_CATCH(...) \ 114 catch (std::exception const &e) \ 115 { \ 116 U_LOG_E("Caught exception: %s", e.what()); \ 117 return __VA_ARGS__; \ 118 } \ 119 catch (...) \ 120 { \ 121 U_LOG_E("Caught exception"); \ 122 return __VA_ARGS__; \ 123 } 124 125struct u_id_ringbuffer * 126u_id_ringbuffer_create(uint32_t capacity) 127{ 128 try { 129 auto ret = std::make_unique<u_id_ringbuffer>(capacity); 130 return ret.release(); 131 } 132 DEFAULT_CATCH(nullptr) 133} 134 135// Common wrapping to catch exceptions in functions that return an index or negative for error. 136template <typename F> 137static inline int64_t 138exceptionCatchingWrapper(F &&func) 139{ 140 try { 141 return func(); 142 } catch (std::exception const &e) { 143 return -1; 144 } 145} 146 147 148int64_t 149u_id_ringbuffer_push_back(struct u_id_ringbuffer *uirb, uint64_t id) 150{ 151 try { 152 auto inner_index = uirb->helper.push_back_location(); 153 uirb->ids[inner_index] = id; 154 return static_cast<int32_t>(inner_index); 155 } 156 DEFAULT_CATCH(-1) 157} 158 159void 160u_id_ringbuffer_pop_front(struct u_id_ringbuffer *uirb) 161{ 162 try { 163 uirb->helper.pop_front(); 164 } 165 DEFAULT_CATCH() 166} 167 168void 169u_id_ringbuffer_pop_back(struct u_id_ringbuffer *uirb) 170{ 171 try { 172 uirb->helper.pop_back(); 173 } 174 DEFAULT_CATCH() 175} 176 177 178int32_t 179u_id_ringbuffer_get_back(struct u_id_ringbuffer *uirb, uint64_t *out_id) 180{ 181 try { 182 auto inner_index = uirb->helper.back_inner_index(); 183 if (inner_index == uirb->capacity) { 184 return -1; 185 } 186 *out_id = uirb->ids[inner_index]; 187 return static_cast<int32_t>(inner_index); 188 } 189 DEFAULT_CATCH(-1) 190} 191 192int32_t 193u_id_ringbuffer_get_front(struct u_id_ringbuffer *uirb, uint64_t *out_id) 194{ 195 196 try { 197 auto inner_index = uirb->helper.front_inner_index(); 198 if (inner_index == uirb->capacity) { 199 return -1; 200 } 201 *out_id = uirb->ids[inner_index]; 202 return static_cast<int32_t>(inner_index); 203 } 204 DEFAULT_CATCH(-1) 205} 206 207uint32_t 208u_id_ringbuffer_get_size(struct u_id_ringbuffer const *uirb) 209{ 210 try { 211 return static_cast<int32_t>(uirb->helper.size()); 212 } 213 DEFAULT_CATCH(0) 214} 215 216bool 217u_id_ringbuffer_is_empty(struct u_id_ringbuffer const *uirb) 218{ 219 try { 220 return uirb->helper.empty(); 221 } 222 DEFAULT_CATCH(true) 223} 224 225// Handle conditional setting of value pointed to by out_id, and casting of inner_index 226static inline int32_t 227handleGetterResult(struct u_id_ringbuffer const *uirb, size_t inner_index, uint64_t *out_id) 228{ 229 if (out_id != nullptr) { 230 *out_id = uirb->ids[inner_index]; 231 } 232 return static_cast<int32_t>(inner_index); 233} 234 235int32_t 236u_id_ringbuffer_get_at_age(struct u_id_ringbuffer *uirb, uint32_t age, uint64_t *out_id) 237{ 238 try { 239 size_t inner_index = 0; 240 if (!uirb->helper.age_to_inner_index(age, inner_index)) { 241 // out of range 242 return -1; 243 } 244 return handleGetterResult(uirb, inner_index, out_id); 245 } 246 DEFAULT_CATCH(-1) 247} 248 249int32_t 250u_id_ringbuffer_get_at_clamped_age(struct u_id_ringbuffer *uirb, uint32_t age, uint64_t *out_id) 251{ 252 try { 253 size_t inner_index = 0; 254 if (!uirb->helper.clamped_age_to_inner_index(age, inner_index)) { 255 // out of range 256 return -1; 257 } 258 return handleGetterResult(uirb, inner_index, out_id); 259 } 260 DEFAULT_CATCH(-1) 261} 262 263int32_t 264u_id_ringbuffer_get_at_index(struct u_id_ringbuffer *uirb, uint32_t index, uint64_t *out_id) 265{ 266 try { 267 size_t inner_index = 0; 268 if (!uirb->helper.index_to_inner_index(index, inner_index)) { 269 // out of range 270 return -1; 271 } 272 return handleGetterResult(uirb, inner_index, out_id); 273 } 274 DEFAULT_CATCH(-1) 275} 276 277// Handle validity of iterators, conditional setting of values pointed to by out_id and out_index, and return value 278static inline int32_t 279handleAlgorithmResult(IdRingbufferIterator it, uint64_t *out_id, uint32_t *out_index) 280{ 281 if (!it.valid()) { 282 return -1; 283 } 284 if (out_id != nullptr) { 285 *out_id = *it; 286 } 287 if (out_index != nullptr) { 288 *out_index = static_cast<uint32_t>(it.index()); 289 } 290 return it.inner_index(); 291} 292 293int32_t 294u_id_ringbuffer_lower_bound_id(struct u_id_ringbuffer *uirb, uint64_t search_id, uint64_t *out_id, uint32_t *out_index) 295{ 296 try { 297 298 const auto b = IdRingbufferIterator::begin(*uirb); 299 const auto e = IdRingbufferIterator::end(*uirb); 300 301 // find the first element *not less than* our ID: binary search 302 const auto it = std::lower_bound(b, e, search_id); 303 return handleAlgorithmResult(it, out_id, out_index); 304 } 305 DEFAULT_CATCH(-1) 306} 307 308int32_t 309u_id_ringbuffer_find_id_unordered(struct u_id_ringbuffer *uirb, 310 uint64_t search_id, 311 uint64_t *out_id, 312 uint32_t *out_index) 313{ 314 try { 315 316 const auto b = IdRingbufferIterator::begin(*uirb); 317 const auto e = IdRingbufferIterator::end(*uirb); 318 319 // find the matching ID with simple linear search 320 const auto it = std::find(b, e, search_id); 321 return handleAlgorithmResult(it, out_id, out_index); 322 } 323 DEFAULT_CATCH(-1) 324} 325 326void 327u_id_ringbuffer_destroy(struct u_id_ringbuffer **ptr_to_uirb) 328{ 329 try { 330 if (ptr_to_uirb == nullptr) { 331 return; 332 } 333 struct u_id_ringbuffer *uirb = *ptr_to_uirb; 334 if (uirb == nullptr) { 335 return; 336 } 337 delete uirb; 338 *ptr_to_uirb = nullptr; 339 } 340 DEFAULT_CATCH() 341}