LLVM OpenMP* Runtime Library
kmp_adt.h
1/*
2 * kmp_adt.h -- Advanced Data Types used internally
3 *
4 * FIXME: This is in intermediate solution until we agree and implement some
5 * common resource according to
6 * https://discourse.llvm.org/t/meta-rfc-adts-without-c-runtime-dependency/90317.
7 * As soon as we will have this common resource that can be used for runtimes
8 * such as openmp that want to avoid the link dependency to the C++ STL, this
9 * shall be refactored.
10 */
11
12//===----------------------------------------------------------------------===//
13//
14// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
15// See https://llvm.org/LICENSE.txt for license information.
16// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
17//
18//===----------------------------------------------------------------------===//
19
20#ifndef KMP_ADT_H
21#define KMP_ADT_H
22
23#include <cassert>
24#include <cctype>
25#include <cstddef>
26#include <cstdint>
27#include <cstring>
28#include <memory>
29#include <type_traits>
30
31#include "kmp.h"
32
34class kmp_str_ref final {
35 const char *data;
36 size_t len;
37
38public:
39 static constexpr size_t npos = SIZE_MAX;
40
41 kmp_str_ref(const char *str) : data(str), len(str ? strlen(str) : 0) {}
42 kmp_str_ref(const char *str, size_t len) : data(str), len(len) {
43 assert((data || !len) && "len must be 0 for nullptr data");
44 }
45
46 kmp_str_ref(const kmp_str_ref &other) = default;
47 kmp_str_ref &operator=(const kmp_str_ref &other) = default;
48
52 if (len < prefix.len)
53 return false;
54 if (empty() || prefix.empty()) // avoid calling memcmp on potential nullptr
55 return true;
56 if (memcmp(data, prefix.data, prefix.len) != 0)
57 return false;
58 drop_front(prefix.len);
59 return true;
60 }
61
65 bool consume_integer(int &value, bool allow_zero = true,
66 bool allow_negative = false);
67
70 char *copy() const;
71
74 template <typename Fn> size_t count_while(const Fn &predicate) const {
75 static_assert(std::is_invocable_r_v<bool, Fn, char>,
76 "predicate must be callable as bool(char)");
77 size_t n = find_if_not(predicate);
78 return n == npos ? len : n;
79 }
80
83 void drop_front(size_t n) {
84 if (n > len)
85 n = len;
86 data += n;
87 len -= n;
88 }
89
91 template <typename Fn> void drop_while(const Fn &predicate) {
92 static_assert(std::is_invocable_r_v<bool, Fn, char>,
93 "predicate must be callable as bool(char)");
94 drop_front(count_while(predicate));
95 }
96
98 bool empty() const { return len == 0; }
99
103 template <typename Fn> size_t find_if(const Fn &predicate) const {
104 static_assert(std::is_invocable_r_v<bool, Fn, char>,
105 "predicate must be callable as bool(char)");
106 size_t i = 0;
107 while (i < len && !predicate(data[i]))
108 ++i;
109 return i < len ? i : npos;
110 }
111
115 template <typename Fn> size_t find_if_not(const Fn &predicate) const {
116 static_assert(std::is_invocable_r_v<bool, Fn, char>,
117 "predicate must be callable as bool(char)");
118 return find_if([predicate](char c) { return !predicate(c); });
119 }
120
122 size_t length() const { return len; }
123 size_t size() const { return length(); }
124
126 void skip_space() {
127 drop_while([](char c) {
128 return static_cast<bool>(isspace(static_cast<unsigned char>(c)));
129 });
130 }
131
134 template <typename Fn> kmp_str_ref take_while(const Fn &predicate) const {
135 static_assert(std::is_invocable_r_v<bool, Fn, char>,
136 "predicate must be callable as bool(char)");
137 return kmp_str_ref(data, count_while(predicate));
138 }
139
141 const char *begin() const { return data; }
142 const char *end() const { return data + len; }
143};
144
148template <typename T, size_t INLINE_THRESHOLD = 8> class kmp_vector final {
149 static_assert(std::is_copy_constructible_v<T>,
150 "T must be copy constructible");
151 static_assert(std::is_destructible_v<T>, "T must be destructible");
152
153 struct default_eq {
154 bool operator()(const T &a, const T &b) const { return a == b; }
155 };
156
157 T inline_data[INLINE_THRESHOLD];
158 T *data = inline_data;
159 size_t count = 0;
160 size_t capacity = INLINE_THRESHOLD;
161
162 void copy_data(T *dst, const T *src, size_t num_elements) {
163 if constexpr (std::is_trivially_copyable_v<T>) {
164 memcpy(dst, src, num_elements * sizeof(T));
165 } else {
166 for (size_t i = 0; i < num_elements; i++)
167 new (&dst[i]) T(src[i]); // copy-construct to memory
168 }
169 }
170
174 void grow(size_t MinSize = 0) {
175 if (MinSize) {
176 if (MinSize <= capacity)
177 return;
178 capacity = MinSize;
179 } else {
180 capacity = capacity + (capacity / 2) + 1;
181 }
182 T *old_data = data != inline_data ? data : nullptr;
183 data =
184 static_cast<T *>(KMP_INTERNAL_REALLOC(old_data, capacity * sizeof(T)));
185 if (!data)
186 KMP_FATAL(MemoryAllocFailed);
187 // Copy the data to the new array if we didn't use a dynamic array before.
188 if (!old_data)
189 copy_data(data, inline_data, count);
190 }
191
192 void init(size_t new_capacity, const T *init_data, size_t new_count) {
193 assert(new_capacity >= new_count &&
194 "more elements requested than capacity");
195 if (new_capacity > capacity)
196 grow(new_capacity);
197 if (init_data)
198 copy_data(data, init_data, new_count);
199 count = new_count;
200 }
201
203 void move_from(kmp_vector &&other) {
204 assert(empty() && "must be empty before overwriting");
205 if (other.data == other.inline_data) {
206 // Cannot move inline data, must copy.
207 init(other.capacity, other.data, other.count);
208 } else {
209 // Steal dynamic data.
210 data = other.data;
211 count = other.count;
212 capacity = other.capacity;
213 }
214 other.reset(/*free_data=*/false);
215 }
216
217 void reset(bool free_data) {
218 if (free_data && data != inline_data) {
219 clear();
220 KMP_INTERNAL_FREE(data);
221 }
222 data = inline_data;
223 count = 0;
224 capacity = INLINE_THRESHOLD;
225 }
226
227public:
228 ~kmp_vector() { reset(/*free_data=*/true); }
229
230 explicit kmp_vector(size_t capacity = 0) { init(capacity, nullptr, 0); }
231
232 kmp_vector(size_t capacity, const T *init_data, size_t count) {
233 init(capacity, init_data, count);
234 }
235
236 kmp_vector(const kmp_vector &other) {
237 init(other.capacity, other.data, other.count);
238 }
239
240 kmp_vector(kmp_vector &&other) noexcept { move_from(std::move(other)); }
241
242 kmp_vector &operator=(const kmp_vector &other) {
243 if (this != &other) {
244 reset(/*free_data=*/true);
245 init(other.capacity, other.data, other.count);
246 }
247 return *this;
248 }
249
250 kmp_vector &operator=(kmp_vector &&other) noexcept {
251 if (this != &other) {
252 reset(/*free_data=*/true);
253 move_from(std::move(other));
254 }
255 return *this;
256 }
257
259 void clear() {
260 if constexpr (!std::is_trivially_destructible_v<T>) {
261 for (size_t i = 0; i < count; i++)
262 data[i].~T();
263 }
264 count = 0;
265 }
266
270 template <typename Fn = default_eq>
271 bool contains(const T &value, const Fn &comp = Fn{}) const {
272 static_assert(std::is_invocable_r_v<bool, Fn, const T &, const T &>,
273 "predicate must be callable as bool(const T &, const T &)");
274 for (size_t i = 0; i < count; i++) {
275 if (comp(data[i], value))
276 return true;
277 }
278 return false;
279 }
280
281 bool empty() const { return !count; }
282
286 template <typename Fn = default_eq>
287 bool is_set_equal(const kmp_vector &other, const Fn &comp = Fn{}) const {
288 static_assert(std::is_invocable_r_v<bool, Fn, const T &, const T &>,
289 "predicate must be callable as bool(const T &, const T &)");
290 for (const T &val : *this) {
291 if (!other.contains(val, comp))
292 return false;
293 }
294 for (const T &val : other) {
295 if (!contains(val, comp))
296 return false;
297 }
298 return true;
299 }
300
302 void push_back(const T &value) {
303 if (count == capacity)
304 grow();
305 if constexpr (std::is_trivially_copyable_v<T>)
306 data[count++] = value;
307 else
308 new (&data[count++]) T(value);
309 }
310
313 void reserve(size_t new_capacity) {
314 if (new_capacity > capacity)
315 grow(new_capacity);
316 }
317
318 size_t size() const { return count; }
319
320 T &operator[](size_t index) {
321 assert(index < count && "Index out of bounds");
322 return data[index];
323 }
324 const T &operator[](size_t index) const {
325 assert(index < count && "Index out of bounds");
326 return data[index];
327 }
328
330 T *begin() { return data; }
331 T *end() { return data + count; }
332 const T *begin() const { return data; }
333 const T *end() const { return data + count; }
334};
335
336#endif // KMP_ADT_H
kmp_str_ref is a non-owning string class (similar to llvm::StringRef).
Definition: kmp_adt.h:34
kmp_str_ref take_while(const Fn &predicate) const
Definition: kmp_adt.h:134
size_t find_if_not(const Fn &predicate) const
Definition: kmp_adt.h:115
size_t length() const
Get the length of the string.
Definition: kmp_adt.h:122
bool consume_front(kmp_str_ref prefix)
Definition: kmp_adt.h:51
char * copy() const
Definition: kmp_adt.cpp:56
bool consume_integer(int &value, bool allow_zero=true, bool allow_negative=false)
Definition: kmp_adt.cpp:27
bool empty() const
Check if the string is empty.
Definition: kmp_adt.h:98
size_t find_if(const Fn &predicate) const
Definition: kmp_adt.h:103
size_t count_while(const Fn &predicate) const
Definition: kmp_adt.h:74
const char * begin() const
Iterator support (raw pointers work as iterators for contiguous storage)
Definition: kmp_adt.h:141
void skip_space()
Drop space from the start of the string.
Definition: kmp_adt.h:126
void drop_front(size_t n)
Definition: kmp_adt.h:83
void drop_while(const Fn &predicate)
Drop characters from the string while the predicate returns true.
Definition: kmp_adt.h:91
void move_from(kmp_vector &&other)
Move data from other vector to this vector (which must be emptied before)
Definition: kmp_adt.h:203
void clear()
Destroy all elements in the vector. Doesn't free the memory.
Definition: kmp_adt.h:259
T * begin()
Iterator support (raw pointers work as iterators for contiguous storage)
Definition: kmp_adt.h:330
void push_back(const T &value)
Add a new element to the end of the vector.
Definition: kmp_adt.h:302
void grow(size_t MinSize=0)
Definition: kmp_adt.h:174
void reserve(size_t new_capacity)
Definition: kmp_adt.h:313
bool is_set_equal(const kmp_vector &other, const Fn &comp=Fn{}) const
Definition: kmp_adt.h:287
bool contains(const T &value, const Fn &comp=Fn{}) const
Definition: kmp_adt.h:271