Monado OpenXR Runtime
u_extension_list.hpp
Go to the documentation of this file.
1// Copyright 2021, Collabora, Ltd.
2// Copyright 2025-2026, NVIDIA CORPORATION.
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief A collection of strings, like a list of extensions to enable
7 *
8 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
9 * @ingroup aux_util
10 *
11 */
12
13#pragma once
14
15#include "u_extension_list.h"
16
17#include <memory>
18#include <vector>
19#include <limits>
20#include <stdexcept>
21#include <string>
22#include <string_view>
23#include <algorithm>
24#include <unordered_set>
25
26namespace xrt::auxiliary::util {
27
28// Forward declaration
29class ExtensionListBuilder;
30
31/*!
32 * @brief An immutable collection of strings, like a list of extensions to enable.
33 *
34 * This class stores copies of strings internally and provides read-only access.
35 *
36 * Size is limited to one less than the max value of uint32_t which shouldn't be a problem,
37 * the size really should be much smaller.
38 */
40{
41public:
42 //! Default constructor - creates an empty list
43 ExtensionList() = default;
44
45 //! Move constructor
47
48 //! Copy constructor, makes sure to do a deep copy.
49 ExtensionList(ExtensionList const &other) : ExtensionList({other.strings.begin(), other.strings.end()}) {}
50
51 //! Move assignment
53 operator=(ExtensionList &&) = default;
54
55 //! Copy assignment
58 {
59 /*
60 * This uses the move assignment to set this to a deep copy of
61 * the other list. We need to do a deep copy of the other list
62 * so that the set and pointers are correctly recalculated using
63 * the new strings.
64 */
65 return *this = ExtensionList(other);
66 }
67
68 /*!
69 * @brief Get the size of the array (the number of strings)
70 */
71 uint32_t
72 size() const noexcept
73 {
74 return static_cast<uint32_t>(ptrs.size());
75 }
76
77 /*!
78 * @brief Get the data pointer of the array (array of const char*)
79 */
80 const char *const *
81 data() const noexcept
82 {
83 return ptrs.data();
84 }
85
86 /*!
87 * @brief Check if the string is in the list.
88 *
89 * (Comparing string contents)
90 *
91 * @param str a string view to search for.
92 *
93 * @return true if the string is in the list.
94 */
95 bool
96 contains(std::string_view str) const
97 {
98 return set.find(str) != set.end();
99 }
100
101
102private:
103 friend class ExtensionListBuilder;
104
105 //! Main storage of strings.
106 std::vector<std::string> strings;
107
108 /*!
109 * Internal set of strings for fast lookup, memory kept alive by the
110 * strings vector. Used to quickly check if a string is in the list.
111 */
112 std::unordered_set<std::string_view> set;
113
114 /*!
115 * Internal vector of pointers to the strings, memory kept alive by the
116 * strings vector.
117 */
118 std::vector<const char *> ptrs;
119
120
121private:
122 // Internal constructor used by ExtensionListBuilder
123 ExtensionList(std::vector<std::string> &&strings_) : strings(std::move(strings_))
124 {
125 ptrs.reserve(strings.size());
126 for (const auto &s : strings) {
127 set.insert(s);
128 ptrs.push_back(s.c_str());
129 }
130 }
131};
132
133/*!
134 * @brief A builder for constructing ExtensionList objects.
135 *
136 * This class allows write-only operations to build up an extension list,
137 * which can then be converted to an immutable ExtensionList.
138 */
140{
141public:
142 //! Default constructor
144
145 //! Construct with capacity hint
146 explicit ExtensionListBuilder(uint32_t capacity)
147 {
148 strings.reserve(capacity);
149 }
150
151 //! Construct from an array of strings
152 template <uint32_t N> explicit ExtensionListBuilder(const char *(&arr)[N])
153 {
154 strings.reserve(N);
155 for (auto &&elt : arr) {
156 append(elt);
157 }
158 }
159
162
164 operator=(ExtensionListBuilder &&) = delete;
166 operator=(ExtensionListBuilder const &) = delete;
167
168 /*!
169 * @brief Append a new string to the builder.
170 *
171 * @param str a string view to append.
172 *
173 * @throws std::out_of_range if you have a ridiculous number of strings in your list already.
174 */
175 void
176 append(std::string_view str)
177 {
178 if (strings.size() > (std::numeric_limits<uint32_t>::max)() - 1) {
179 throw std::out_of_range("Size limit reached");
180 }
181 strings.emplace_back(str);
182 }
183
184 /*!
185 * @brief Append a new string to the builder if it doesn't match any existing string.
186 *
187 * (Comparing string contents)
188 *
189 * @param str a string view to append.
190 *
191 * @return true if we added it
192 *
193 * @throws std::out_of_range if you have a ridiculous number of strings in your list already.
194 */
195 bool
196 appendUnique(std::string_view str)
197 {
198 if (strings.size() > (std::numeric_limits<uint32_t>::max)() - 1) {
199 throw std::out_of_range("Size limit reached");
200 }
201 auto it =
202 std::find_if(strings.begin(), strings.end(), [str](const std::string &elt) { return str == elt; });
203 if (it != strings.end()) {
204 // already have it
205 return false;
206 }
207 strings.emplace_back(str);
208 return true;
209 }
210
211 /*!
212 * @brief Sort the strings in the builder suitable for extension lists.
213 *
214 * The list will be sorted first by API (VK, XR, etc.), then all KHR extensions,
215 * then all EXT extensions, then all Vendor extensions, then all experimental
216 * extensions. (Alphabetical within each group.)
217 *
218 * This modifies the builder in-place.
219 */
220 void
222
223 /*!
224 * @brief Build and return an immutable ExtensionList.
225 *
226 * This function consumes the builder by moving its internal data into
227 * the returned ExtensionList. After calling this, the builder is left in a
228 * valid but empty state.
229 *
230 * The && qualifier (rvalue reference qualifier) means this can only be
231 * called on rvalue references, i.e. on:
232 * - Temporaries: `ExtensionListBuilder().build()`
233 * - Moved objects: `std::move(builder).build()`
234 *
235 * This prevents accidentally calling build() twice on the same builder,
236 * which would be a bug.
237 */
239 build() &&
240 {
241 return ExtensionList(std::move(strings));
242 }
243
244
245private:
246 std::vector<std::string> strings;
247};
248
249} // namespace xrt::auxiliary::util
A builder for constructing ExtensionList objects.
Definition: u_extension_list.hpp:140
void append(std::string_view str)
Append a new string to the builder.
Definition: u_extension_list.hpp:176
bool appendUnique(std::string_view str)
Append a new string to the builder if it doesn't match any existing string.
Definition: u_extension_list.hpp:196
ExtensionListBuilder(uint32_t capacity)
Construct with capacity hint.
Definition: u_extension_list.hpp:146
ExtensionListBuilder()=default
Default constructor.
ExtensionListBuilder(const char *(&arr)[N])
Construct from an array of strings.
Definition: u_extension_list.hpp:152
void sortForExtensions()
Sort the strings in the builder suitable for extension lists.
Definition: u_extension_list.cpp:265
ExtensionList build() &&
Build and return an immutable ExtensionList.
Definition: u_extension_list.hpp:239
An immutable collection of strings, like a list of extensions to enable.
Definition: u_extension_list.hpp:40
uint32_t size() const noexcept
Get the size of the array (the number of strings)
Definition: u_extension_list.hpp:72
ExtensionList(ExtensionList &&)=default
Move constructor.
ExtensionList()=default
Default constructor - creates an empty list.
bool contains(std::string_view str) const
Check if the string is in the list.
Definition: u_extension_list.hpp:96
ExtensionList & operator=(ExtensionList const &other)
Copy assignment.
Definition: u_extension_list.hpp:57
ExtensionList(ExtensionList const &other)
Copy constructor, makes sure to do a deep copy.
Definition: u_extension_list.hpp:49
ExtensionList & operator=(ExtensionList &&)=default
Move assignment.
const char *const * data() const noexcept
Get the data pointer of the array (array of const char*)
Definition: u_extension_list.hpp:81
A collection of strings, like a list of extensions to enable.