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 
34 class kmp_str_ref final {
35  const char *data;
36  size_t len;
37 
38 public:
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 
51  bool consume_front(kmp_str_ref prefix) {
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 
148 template <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 
227 public:
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
const char * begin() const
Iterator support (raw pointers work as iterators for contiguous storage)
Definition: kmp_adt.h:141
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
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