The open source OpenXR runtime
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}