llvm-mos-sdk
soa.h
Go to the documentation of this file.
1 #ifndef _SOA_H
2 #define _SOA_H
3 
4 #include <array>
5 #include <cstdint>
6 #include <initializer_list>
7 #include <new>
8 #include <type_traits>
9 #include <utility>
10 
11 namespace soa {
12 
46 template <typename T, uint8_t N> class Array;
47 
72 template <typename T> struct Ptr;
73 
74 template <class _ToType, class _FromType>
75 constexpr _ToType __bit_cast(const _FromType &__from) {
76  return __builtin_bit_cast(_ToType, __from);
77 }
78 
80 template <typename T> class BasePtr {
81  static_assert(!std::is_volatile_v<T>, "volatile types are not supported");
82  static_assert(std::is_trivial_v<T>, "non-trivial types are unsupported");
83  static_assert(std::is_standard_layout_v<T>,
84  "non-standard layout types are unsupported");
85  static_assert(std::alignment_of_v<T> == 1, "aligned types are not supported");
86 
87 protected:
88  // Pointers to array elements are represented as an array of pointers to
89  // each byte. This keeps the representation agnostic of the size of the
90  // original array.
91  const uint8_t *BytePtrs[sizeof(T)];
92 
93 private:
94  // Initializes the underlying bytes of the array using a tailcall recursive
95  // function. LLVM is able to fully inline this recursive call, and this also
96  // helps preserve the source info for the line when debugging.
97  template <uint8_t N, uint8_t ByteIdx = 0>
98  [[clang::always_inline]] constexpr void initBytePtrs(
99  const uint8_t ByteArrays[][N], uint8_t Idx) {
100  if constexpr (ByteIdx < sizeof(T)) {
101  BytePtrs[ByteIdx] = &ByteArrays[ByteIdx][Idx];
102  initBytePtrs<N, ByteIdx + 1>(ByteArrays, Idx);
103  }
104  }
105 
106 public:
107  template <uint8_t N>
108  [[clang::always_inline]] constexpr BasePtr(const uint8_t ByteArrays[][N],
109  uint8_t Idx) {
110  initBytePtrs<N>(ByteArrays, Idx);
111  }
112 
113  template <uint8_t N>
114  [[clang::always_inline]] constexpr BasePtr(uint8_t ByteArrays[][N],
115  uint8_t Idx) {
116  initBytePtrs<N>(ByteArrays, Idx);
117  }
118 
123  [[clang::always_inline]] T get() const { return static_cast<T>(*this); }
124 
125 private:
126  // Helper function to read the bytes using another tailcall recursive
127  // function. Stores the bytes read into the array passed in as a parameter.
128  template <uint8_t Idx = 0>
129  [[clang::always_inline]] constexpr void readBytes(
130  uint8_t (&Bytes)[sizeof(T)]) const {
131  if constexpr (Idx < sizeof(T)) {
132  Bytes[Idx] = *BytePtrs[Idx];
133  readBytes<Idx + 1>(Bytes);
134  }
135  }
136 
137 public:
138  [[clang::always_inline]] constexpr operator T() const {
139  uint8_t Bytes[sizeof(T)];
140  readBytes(Bytes);
141  return __bit_cast<T>(Bytes);
142  }
143 
144  template <typename... ArgsT>
145  [[clang::always_inline]] auto operator()(ArgsT &&...Args) const -> auto {
146  return static_cast<const T>(get())(std::forward<ArgsT>(Args)...);
147  }
148 
149 private:
150  // Helper function to write the bytes using yet another tailcall recursive
151  // function. Stores the bytes read into the underlying byte array.
152  template <uint8_t Idx = 0>
153  [[clang::always_inline]] void writeBytes(const uint8_t *Bytes) {
154  if constexpr (Idx < sizeof(T)) {
155  *const_cast<uint8_t *>(BytePtrs[Idx]) = Bytes[Idx];
156  writeBytes<Idx + 1>(Bytes);
157  }
158  }
159 
160 public:
161  template <typename Q = T>
162  [[clang::always_inline]] std::enable_if_t<!std::is_const_v<Q>, const Ptr<T> &>
163  operator=(const T &Val) {
164  writeBytes(reinterpret_cast<const uint8_t *>(&Val));
165  return *static_cast<Ptr<T> *>(this);
166  }
167 
168  template <typename Q = T>
169  [[clang::always_inline]] std::enable_if_t<std::is_pointer_v<Q>, const T>
170  operator->() const {
171  return *this;
172  }
173  template <typename Q = T>
174  [[clang::always_inline]] std::enable_if_t<std::is_pointer_v<Q>, T>
176  return *this;
177  }
178 
179 private:
180  class ConstWrapper {
181  const T V;
182 
183  public:
184  [[clang::always_inline]] ConstWrapper(const Ptr<T> &P) : V(P) {}
185  [[clang::always_inline]] const T *operator->() const { return &V; }
186  };
187 
188 public:
189  template <typename Q = T>
190  [[clang::always_inline]] std::enable_if_t<!std::is_pointer_v<Q>, ConstWrapper>
191  operator->() const {
192  return *static_cast<const Ptr<T> *>(this);
193  }
194 
195  template <typename U>
196  [[clang::always_inline]] Ptr<T> &operator+=(const U &Right) {
197  *this = *this + Right;
198  return *static_cast<Ptr<T> *>(this);
199  }
200 
201  template <typename U>
202  [[clang::always_inline]] Ptr<T> &operator-=(const U &Right) {
203  *this = *this - Right;
204  return *static_cast<Ptr<T> *>(this);
205  }
206 
207  template <typename U>
208  [[clang::always_inline]] Ptr<T> &operator*=(const U &Right) {
209  *this = *this * Right;
210  return *static_cast<Ptr<T> *>(this);
211  }
212 
213  template <typename U>
214  [[clang::always_inline]] Ptr<T> &operator/=(const U &Right) {
215  *this = *this / Right;
216  return *static_cast<Ptr<T> *>(this);
217  }
218 
219  template <typename U>
220  [[clang::always_inline]] Ptr<T> &operator%=(const U &Right) {
221  *this = *this % Right;
222  return *static_cast<Ptr<T> *>(this);
223  }
224 
225  template <typename U>
226  [[clang::always_inline]] Ptr<T> &operator^=(const U &Right) {
227  *this = *this ^ Right;
228  return *static_cast<Ptr<T> *>(this);
229  }
230 
231  template <typename U>
232  [[clang::always_inline]] Ptr<T> &operator&=(const U &Right) {
233  *this = *this & Right;
234  return *static_cast<Ptr<T> *>(this);
235  }
236 
237  template <typename U>
238  [[clang::always_inline]] Ptr<T> &operator|=(const U &Right) {
239  *this = *this | Right;
240  return *static_cast<Ptr<T> *>(this);
241  }
242 
243  template <typename U>
244  [[clang::always_inline]] Ptr<T> &operator<<=(const U &Right) {
245  *this = *this << Right;
246  return *static_cast<Ptr<T> *>(this);
247  }
248 
249  template <typename U>
250  [[clang::always_inline]] Ptr<T> &operator>>=(const U &Right) {
251  *this = *this >> Right;
252  return *static_cast<Ptr<T> *>(this);
253  }
254 
255  [[clang::always_inline]] Ptr<T> &operator++() {
256  *this += 1;
257  return *static_cast<Ptr<T> *>(this);
258  }
259  [[clang::always_inline]] Ptr<T> &operator--() {
260  *this -= 1;
261  return *static_cast<Ptr<T> *>(this);
262  }
263 
264  [[clang::always_inline]] T operator++(int) {
265  T old = *this;
266  ++*this;
267  return *static_cast<Ptr<T> *>(this);
268  }
269 
270  [[clang::always_inline]] T operator--(int) {
271  T old = *this;
272  --*this;
273  return *static_cast<Ptr<T> *>(this);
274  }
275 };
276 
277 template <typename T> class Ptr : public BasePtr<T> {
278 public:
279  using BasePtr<T>::BasePtr;
280  using BasePtr<T>::operator=;
281 };
282 
283 template <typename T, uint8_t N> class Ptr<T[N]> {
284  static_assert(!std::is_volatile_v<T>, "volatile types are not supported");
285  static_assert(std::is_trivial_v<T>, "non-trivial types are unsupported");
286  static_assert(std::is_standard_layout_v<T>,
287  "non-standard layout types are unsupported");
288  static_assert(std::alignment_of_v<T> == 1, "aligned types are not supported");
289 
290  uint8_t PtrStorage[sizeof(Ptr<T>[N])];
291  [[clang::always_inline]] Ptr<T> *ptrs() {
292  return reinterpret_cast<Ptr<T> *>(PtrStorage);
293  }
294 
295  // Helper function to initalize the array for internal multibyte arrays
296  // See also: BasePtr::initBytePtrs
297  template <uint8_t M, uint8_t ArrayIdx = 0>
298  [[clang::always_inline]] constexpr void initPtrs(const uint8_t ByteArrays[][M],
299  uint8_t Idx) {
300  if constexpr (ArrayIdx < N) {
301  new (&ptrs()[ArrayIdx]) Ptr<T>(ByteArrays + ArrayIdx * sizeof(T), Idx);
302  initPtrs<M, ArrayIdx + 1>(ByteArrays, Idx);
303  }
304  }
305 
306 public:
307  template <uint8_t M>
308  [[clang::always_inline]] constexpr Ptr(const uint8_t ByteArrays[][M],
309  uint8_t Idx) {
310  initPtrs<M>(ByteArrays, Idx);
311  }
312 
313  template <uint8_t M>
314  [[clang::always_inline]] constexpr Ptr(uint8_t ByteArrays[][M], uint8_t Idx) {
315  initPtrs<M>(ByteArrays, Idx);
316  }
317 
318  Ptr<const T> operator[](uint8_t Idx) const { return ptrs()[Idx]; }
319  Ptr<T> operator[](uint8_t Idx) { return ptrs()[Idx]; }
320 };
321 
322 template <typename T, uint8_t N> class ArrayConstIterator {
323  friend class Array<T, N>;
324 
325 protected:
326  const Array<T, N> &A;
328 
329  [[clang::always_inline]] ArrayConstIterator(const Array<T, N> &A, uint8_t Idx)
330  : A(A), Idx(Idx) {}
331 
332 public:
333  [[clang::always_inline]] Ptr<const T> operator*() const { return A[Idx]; }
334 
335  [[clang::always_inline]] ArrayConstIterator &operator++() {
336  ++Idx;
337  return *this;
338  }
339 
340  bool operator==(const ArrayConstIterator &Other) const {
341  return &A == &Other.A && Idx == Other.Idx;
342  }
343  bool operator!=(const ArrayConstIterator &Other) const {
344  return !(*this == Other);
345  }
346 };
347 
348 template <typename T, uint8_t N>
349 class ArrayIterator : public ArrayConstIterator<T, N> {
350  friend class Array<T, N>;
351 
354 
355  [[clang::always_inline]] ArrayIterator(Array<T, N> &A, uint8_t Idx)
357 
358 public:
359  [[clang::always_inline]] Ptr<T> operator*() const {
360  return const_cast<soa::Array<T, N> &>(A)[Idx];
361  }
362 
363  [[clang::always_inline]] ArrayIterator &operator++() {
365  return *this;
366  }
367 };
368 
369 template <typename T, uint8_t N> class Array {
370  static_assert(!std::is_volatile_v<T>, "volatile types are not supported");
371  static_assert(std::is_trivial_v<T>, "non-trivial types are unsupported");
372  static_assert(std::is_standard_layout_v<T>,
373  "only standard layout types are supported");
374  static_assert(std::alignment_of_v<T> == 1, "aligned types are not supported");
375 
376  uint8_t ByteArrays[sizeof(T)][N];
377 
378  // Helper fucntion to recursively store the bytes from a multibyte array
379  template <uint8_t ByteIdx = 0>
380  [[clang::always_inline]] constexpr void storeEntryBytes(
381  uint8_t Idx, const std::array<const uint8_t, sizeof(T)> &Bytes) {
382  if constexpr (ByteIdx < sizeof(T)) {
383  ByteArrays[ByteIdx][Idx] = Bytes[ByteIdx];
384  storeEntryBytes<ByteIdx + 1>(Idx, Bytes);
385  }
386  }
387 
388 public:
389  // Note: This partially duplicates the logic in Ptr::operator=, but works for
390  // types like multidimensional arrays where assignment isn't defined.
391  [[clang::always_inline]] constexpr Array(std::initializer_list<T> Entries) {
392  uint8_t Idx = 0;
393  for (const T &Entry : Entries) {
394  storeEntryBytes(Idx,
395  __bit_cast<std::array<const uint8_t, sizeof(T)>>(Entry));
396  ++Idx;
397  }
398  }
399 
400  // Arrays cannot be assigned with operator= above, so provide a specific out
401  // to initialize them with nested initializer lists.
402  [[clang::always_inline]] constexpr Array() = default;
403 
404  template <uint8_t M>
405  [[clang::always_inline]] constexpr Array(const Array<T, M> &Other) {
406  memcpy(ByteArrays, Other.ByteArrays, sizeof(Other.ByteArrays));
407  }
408 
409  [[clang::always_inline]] constexpr Ptr<T> operator[](uint8_t Idx) {
410  return {ByteArrays, Idx};
411  }
412  [[clang::always_inline]] constexpr Ptr<const T>
413  operator[](uint8_t Idx) const {
414  return {ByteArrays, Idx};
415  }
416 
417  [[clang::always_inline]] constexpr ArrayConstIterator<T, N> begin() const {
418  return {*this, 0};
419  }
420  [[clang::always_inline]] constexpr ArrayConstIterator<T, N> end() const {
421  return {*this, size()};
422  }
423 
424  [[clang::always_inline]] constexpr ArrayIterator<T, N> begin() {
425  return {*this, 0};
426  }
427  [[clang::always_inline]] constexpr ArrayIterator<T, N> end() {
428  return {*this, size()};
429  }
430 
431  [[clang::always_inline]] constexpr uint8_t size() const { return N; }
432 };
433 
434 } // namespace soa
435 
436 #endif // _SOA_H
Definition: soa.h:322
uint8_t Idx
Definition: soa.h:327
const Array< T, N > & A
Definition: soa.h:326
bool operator==(const ArrayConstIterator &Other) const
Definition: soa.h:340
bool operator!=(const ArrayConstIterator &Other) const
Definition: soa.h:343
ArrayConstIterator(const Array< T, N > &A, uint8_t Idx)
Definition: soa.h:329
ArrayConstIterator & operator++()
Definition: soa.h:335
Ptr< const T > operator*() const
Definition: soa.h:333
Definition: soa.h:349
ArrayIterator & operator++()
Definition: soa.h:363
Ptr< T > operator*() const
Definition: soa.h:359
Definition: soa.h:369
constexpr Array()=default
constexpr ArrayConstIterator< T, N > end() const
Definition: soa.h:420
constexpr ArrayIterator< T, N > end()
Definition: soa.h:427
constexpr ArrayConstIterator< T, N > begin() const
Definition: soa.h:417
constexpr Ptr< T > operator[](uint8_t Idx)
Definition: soa.h:409
constexpr ArrayIterator< T, N > begin()
Definition: soa.h:424
constexpr Ptr< const T > operator[](uint8_t Idx) const
Definition: soa.h:413
constexpr Array(const Array< T, M > &Other)
Definition: soa.h:405
constexpr Array(std::initializer_list< T > Entries)
Definition: soa.h:391
constexpr uint8_t size() const
Definition: soa.h:431
Base class for pointer to array elements.
Definition: soa.h:80
std::enable_if_t< std::is_pointer_v< Q >, T > operator->()
Definition: soa.h:175
Ptr< T > & operator^=(const U &Right)
Definition: soa.h:226
std::enable_if_t<!std::is_const_v< Q >, const Ptr< T > & > operator=(const T &Val)
Definition: soa.h:163
Ptr< T > & operator>>=(const U &Right)
Definition: soa.h:250
Ptr< T > & operator<<=(const U &Right)
Definition: soa.h:244
Ptr< T > & operator%=(const U &Right)
Definition: soa.h:220
Ptr< T > & operator+=(const U &Right)
Definition: soa.h:196
T get() const
Definition: soa.h:123
Ptr< T > & operator|=(const U &Right)
Definition: soa.h:238
Ptr< T > & operator-=(const U &Right)
Definition: soa.h:202
const uint8_t * BytePtrs[sizeof(T)]
Definition: soa.h:81
Ptr< T > & operator++()
Definition: soa.h:255
Ptr< T > & operator--()
Definition: soa.h:259
Ptr< T > & operator&=(const U &Right)
Definition: soa.h:232
auto operator()(ArgsT &&...Args) const -> auto
Definition: soa.h:145
Ptr< T > & operator*=(const U &Right)
Definition: soa.h:208
T operator--(int)
Definition: soa.h:270
constexpr BasePtr(const uint8_t ByteArrays[][N], uint8_t Idx)
Definition: soa.h:108
std::enable_if_t< std::is_pointer_v< Q >, const T > operator->() const
Definition: soa.h:170
std::enable_if_t<!std::is_pointer_v< Q >, ConstWrapper > operator->() const
Definition: soa.h:191
Ptr< T > & operator/=(const U &Right)
Definition: soa.h:214
T operator++(int)
Definition: soa.h:264
constexpr BasePtr(uint8_t ByteArrays[][N], uint8_t Idx)
Definition: soa.h:114
Ptr< const T > operator[](uint8_t Idx) const
Definition: soa.h:318
constexpr Ptr(const uint8_t ByteArrays[][M], uint8_t Idx)
Definition: soa.h:308
constexpr Ptr(uint8_t ByteArrays[][M], uint8_t Idx)
Definition: soa.h:314
Definition: initializer_list:8
Definition: soa.h:11
::uint8_t uint8_t
Definition: cstdint:21
void * memcpy(void *__restrict__ s1, const void *__restrict__ s2, size_t n)
Definition: soa.h:277
Definition: array:10