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 public:
94  template <uint8_t N>
95  [[clang::always_inline]] constexpr BasePtr(const uint8_t ByteArrays[][N],
96  uint8_t Idx) {
97 #pragma unroll
98  for (uint8_t ByteIdx = 0; ByteIdx < sizeof(T); ++ByteIdx)
99  BytePtrs[ByteIdx] = &ByteArrays[ByteIdx][Idx];
100  }
101 
102  template <uint8_t N>
103  [[clang::always_inline]] constexpr BasePtr(uint8_t ByteArrays[][N],
104  uint8_t Idx) {
105 #pragma unroll
106  for (uint8_t ByteIdx = 0; ByteIdx < sizeof(T); ++ByteIdx)
107  BytePtrs[ByteIdx] = &ByteArrays[ByteIdx][Idx];
108  }
109 
114  [[clang::always_inline]] T get() const { return static_cast<T>(*this); }
115 
116  [[clang::always_inline]] constexpr operator T() const {
117  uint8_t Bytes[sizeof(T)];
118 #pragma unroll
119  for (uint8_t Idx = 0; Idx < sizeof(T); ++Idx)
120  Bytes[Idx] = *BytePtrs[Idx];
121  return __bit_cast<T>(Bytes);
122  }
123 
124  template <typename... ArgsT>
125  [[clang::always_inline]] auto operator()(ArgsT &&...Args) const -> auto {
126  return static_cast<const T>(get())(std::forward<ArgsT>(Args)...);
127  }
128 
129  template <typename Q = T>
130  [[clang::always_inline]] std::enable_if_t<!std::is_const_v<Q>, const Ptr<T> &>
131  operator=(const T &Val) {
132  auto *Bytes = reinterpret_cast<const uint8_t *>(&Val);
133 #pragma unroll
134  for (uint8_t Idx = 0; Idx < sizeof(T); ++Idx)
135  *const_cast<uint8_t *>(BytePtrs[Idx]) = Bytes[Idx];
136  return *static_cast<Ptr<T> *>(this);
137  }
138 
139  template <typename Q = T>
140  [[clang::always_inline]] std::enable_if_t<std::is_pointer_v<Q>, const T>
141  operator->() const {
142  return *this;
143  }
144  template <typename Q = T>
145  [[clang::always_inline]] std::enable_if_t<std::is_pointer_v<Q>, T>
147  return *this;
148  }
149 
150 private:
151  class ConstWrapper {
152  const T V;
153 
154  public:
155  [[clang::always_inline]] ConstWrapper(const Ptr<T> &P) : V(P) {}
156  [[clang::always_inline]] const T *operator->() const { return &V; }
157  };
158 
159 public:
160  template <typename Q = T>
161  [[clang::always_inline]] std::enable_if_t<!std::is_pointer_v<Q>, ConstWrapper>
162  operator->() const {
163  return *static_cast<const Ptr<T> *>(this);
164  }
165 
166  template <typename U>
167  [[clang::always_inline]] Ptr<T> &operator+=(const U &Right) {
168  *this = *this + Right;
169  return *static_cast<Ptr<T> *>(this);
170  }
171 
172  template <typename U>
173  [[clang::always_inline]] Ptr<T> &operator-=(const U &Right) {
174  *this = *this - Right;
175  return *static_cast<Ptr<T> *>(this);
176  }
177 
178  template <typename U>
179  [[clang::always_inline]] Ptr<T> &operator*=(const U &Right) {
180  *this = *this * Right;
181  return *static_cast<Ptr<T> *>(this);
182  }
183 
184  template <typename U>
185  [[clang::always_inline]] Ptr<T> &operator/=(const U &Right) {
186  *this = *this / Right;
187  return *static_cast<Ptr<T> *>(this);
188  }
189 
190  template <typename U>
191  [[clang::always_inline]] Ptr<T> &operator%=(const U &Right) {
192  *this = *this % Right;
193  return *static_cast<Ptr<T> *>(this);
194  }
195 
196  template <typename U>
197  [[clang::always_inline]] Ptr<T> &operator^=(const U &Right) {
198  *this = *this ^ Right;
199  return *static_cast<Ptr<T> *>(this);
200  }
201 
202  template <typename U>
203  [[clang::always_inline]] Ptr<T> &operator&=(const U &Right) {
204  *this = *this & Right;
205  return *static_cast<Ptr<T> *>(this);
206  }
207 
208  template <typename U>
209  [[clang::always_inline]] Ptr<T> &operator|=(const U &Right) {
210  *this = *this | Right;
211  return *static_cast<Ptr<T> *>(this);
212  }
213 
214  template <typename U>
215  [[clang::always_inline]] Ptr<T> &operator<<=(const U &Right) {
216  *this = *this << Right;
217  return *static_cast<Ptr<T> *>(this);
218  }
219 
220  template <typename U>
221  [[clang::always_inline]] Ptr<T> &operator>>=(const U &Right) {
222  *this = *this >> Right;
223  return *static_cast<Ptr<T> *>(this);
224  }
225 
226  [[clang::always_inline]] Ptr<T> &operator++() {
227  *this += 1;
228  return *static_cast<Ptr<T> *>(this);
229  }
230  [[clang::always_inline]] Ptr<T> &operator--() {
231  *this -= 1;
232  return *static_cast<Ptr<T> *>(this);
233  }
234 
235  [[clang::always_inline]] T operator++(int) {
236  T old = *this;
237  ++*this;
238  return *static_cast<Ptr<T> *>(this);
239  }
240 
241  [[clang::always_inline]] T operator--(int) {
242  T old = *this;
243  --*this;
244  return *static_cast<Ptr<T> *>(this);
245  }
246 };
247 
248 template <typename T> class Ptr : public BasePtr<T> {
249 public:
250  using BasePtr<T>::BasePtr;
251  using BasePtr<T>::operator=;
252 };
253 
254 template <typename T, uint8_t N> class Ptr<T[N]> {
255  static_assert(!std::is_volatile_v<T>, "volatile types are not supported");
256  static_assert(std::is_trivial_v<T>, "non-trivial types are unsupported");
257  static_assert(std::is_standard_layout_v<T>,
258  "non-standard layout types are unsupported");
259  static_assert(std::alignment_of_v<T> == 1, "aligned types are not supported");
260 
261  uint8_t PtrStorage[sizeof(Ptr<T>[N])];
262  [[clang::always_inline]] Ptr<T> *ptrs() {
263  return reinterpret_cast<Ptr<T> *>(PtrStorage);
264  }
265 
266 public:
267  template <uint8_t M>
268  [[clang::always_inline]] constexpr Ptr(const uint8_t ByteArrays[][M],
269  uint8_t Idx) {
270 #pragma unroll
271  for (uint8_t ArrayIdx = 0; ArrayIdx < N; ++ArrayIdx)
272  new (&ptrs()[ArrayIdx]) Ptr<T>(ByteArrays + ArrayIdx * sizeof(T), Idx);
273  }
274 
275  template <uint8_t M>
276  [[clang::always_inline]] constexpr Ptr(uint8_t ByteArrays[][M], uint8_t Idx) {
277 #pragma unroll
278  for (uint8_t ArrayIdx = 0; ArrayIdx < N; ++ArrayIdx)
279  new (&ptrs()[ArrayIdx]) Ptr<T>(ByteArrays + ArrayIdx * sizeof(T), Idx);
280  }
281 
282  Ptr<const T> operator[](uint8_t Idx) const { return ptrs()[Idx]; }
283  Ptr<T> operator[](uint8_t Idx) { return ptrs()[Idx]; }
284 };
285 
286 template <typename T, uint8_t N> class ArrayConstIterator {
287  friend class Array<T, N>;
288 
289 protected:
290  const Array<T, N> &A;
292 
293  [[clang::always_inline]] ArrayConstIterator(const Array<T, N> &A, uint8_t Idx)
294  : A(A), Idx(Idx) {}
295 
296 public:
297  [[clang::always_inline]] Ptr<const T> operator*() const { return A[Idx]; }
298 
299  [[clang::always_inline]] ArrayConstIterator &operator++() {
300  ++Idx;
301  return *this;
302  }
303 
304  bool operator==(const ArrayConstIterator &Other) const {
305  return &A == &Other.A && Idx == Other.Idx;
306  }
307  bool operator!=(const ArrayConstIterator &Other) const {
308  return !(*this == Other);
309  }
310 };
311 
312 template <typename T, uint8_t N>
313 class ArrayIterator : public ArrayConstIterator<T, N> {
314  friend class Array<T, N>;
315 
318 
319  [[clang::always_inline]] ArrayIterator(Array<T, N> &A, uint8_t Idx)
320  : ArrayConstIterator<T, N>(A, Idx) {}
321 
322 public:
323  [[clang::always_inline]] Ptr<T> operator*() const {
324  return const_cast<soa::Array<T, N> &>(A)[Idx];
325  }
326 
327  [[clang::always_inline]] ArrayIterator &operator++() {
329  return *this;
330  }
331 };
332 
333 template <typename T, uint8_t N> class Array {
334  static_assert(!std::is_volatile_v<T>, "volatile types are not supported");
335  static_assert(std::is_trivial_v<T>, "non-trivial types are unsupported");
336  static_assert(std::is_standard_layout_v<T>,
337  "only standard layout types are supported");
338  static_assert(std::alignment_of_v<T> == 1, "aligned types are not supported");
339 
340  uint8_t ByteArrays[sizeof(T)][N];
341 
342 public:
343  // Note: This partially duplicates the logic in Ptr::operator=, but works for
344  // types like multidimensional arrays where assignment isn't defined.
345  [[clang::always_inline]] constexpr Array(std::initializer_list<T> Entries) {
346  uint8_t Idx = 0;
347  for (const T &Entry : Entries) {
348  const auto Bytes =
349  __bit_cast<std::array<const uint8_t, sizeof(T)>>(Entry);
350 #pragma unroll
351  for (uint8_t ByteIdx = 0; ByteIdx < sizeof(T); ++ByteIdx)
352  ByteArrays[ByteIdx][Idx] = Bytes[ByteIdx];
353  ++Idx;
354  }
355  }
356 
357  // Arrays cannot be assigned with operator= above, so provide a specific out
358  // to initialize them with nested initializer lists.
359  [[clang::always_inline]] constexpr Array() = default;
360 
361  template <uint8_t M>
362  [[clang::always_inline]] constexpr Array(const Array<T, M> &Other) {
363  memcpy(ByteArrays, Other.ByteArrays, sizeof(Other.ByteArrays));
364  }
365 
366  [[clang::always_inline]] constexpr Ptr<T> operator[](uint8_t Idx) {
367  return {ByteArrays, Idx};
368  }
369  [[clang::always_inline]] constexpr Ptr<const T>
370  operator[](uint8_t Idx) const {
371  return {ByteArrays, Idx};
372  }
373 
374  [[clang::always_inline]] constexpr ArrayConstIterator<T, N> begin() const {
375  return {*this, 0};
376  }
377  [[clang::always_inline]] constexpr ArrayConstIterator<T, N> end() const {
378  return {*this, size()};
379  }
380 
381  [[clang::always_inline]] constexpr ArrayIterator<T, N> begin() {
382  return {*this, 0};
383  }
384  [[clang::always_inline]] constexpr ArrayIterator<T, N> end() {
385  return {*this, size()};
386  }
387 
388  [[clang::always_inline]] constexpr uint8_t size() const { return N; }
389 };
390 
391 } // namespace soa
392 
393 #endif // _SOA_H
soa::BasePtr::operator->
std::enable_if_t< std::is_pointer_v< Q >, const T > operator->() const
Definition: soa.h:141
soa::BasePtr::operator+=
Ptr< T > & operator+=(const U &Right)
Definition: soa.h:167
soa::BasePtr
Base class for pointer to array elements.
Definition: soa.h:80
soa::Array::begin
constexpr ArrayIterator< T, N > begin()
Definition: soa.h:381
soa::Array::Array
constexpr Array(std::initializer_list< T > Entries)
Definition: soa.h:345
type_traits
utility
soa::ArrayConstIterator
Definition: soa.h:286
soa::BasePtr::operator^=
Ptr< T > & operator^=(const U &Right)
Definition: soa.h:197
soa::ArrayConstIterator::A
const Array< T, N > & A
Definition: soa.h:290
soa::ArrayIterator::operator++
ArrayIterator & operator++()
Definition: soa.h:327
soa
Definition: soa.h:11
soa::BasePtr::operator()
auto operator()(ArgsT &&...Args) const -> auto
Definition: soa.h:125
soa::Array
Definition: soa.h:46
soa::ArrayIterator
Definition: soa.h:313
soa::BasePtr::get
T get() const
Definition: soa.h:114
soa::Array::begin
constexpr ArrayConstIterator< T, N > begin() const
Definition: soa.h:374
soa::ArrayConstIterator::operator==
bool operator==(const ArrayConstIterator &Other) const
Definition: soa.h:304
new
std::uint8_t
::uint8_t uint8_t
Definition: cstdint:21
soa::Array::operator[]
constexpr Ptr< T > operator[](uint8_t Idx)
Definition: soa.h:366
soa::Array::Array
constexpr Array(const Array< T, M > &Other)
Definition: soa.h:362
soa::BasePtr::operator>>=
Ptr< T > & operator>>=(const U &Right)
Definition: soa.h:221
soa::Ptr< T[N]>::Ptr
constexpr Ptr(uint8_t ByteArrays[][M], uint8_t Idx)
Definition: soa.h:276
soa::Ptr
Definition: soa.h:72
soa::BasePtr::operator*=
Ptr< T > & operator*=(const U &Right)
Definition: soa.h:179
soa::Array::end
constexpr ArrayConstIterator< T, N > end() const
Definition: soa.h:377
soa::BasePtr::BasePtr
constexpr BasePtr(const uint8_t ByteArrays[][N], uint8_t Idx)
Definition: soa.h:95
memcpy
void * memcpy(void *__restrict__ s1, const void *__restrict__ s2, size_t n)
array
soa::ArrayConstIterator::Idx
uint8_t Idx
Definition: soa.h:291
initializer_list
cstdint
soa::ArrayConstIterator::operator*
Ptr< const T > operator*() const
Definition: soa.h:297
soa::BasePtr::operator--
Ptr< T > & operator--()
Definition: soa.h:230
soa::BasePtr::operator->
std::enable_if_t< std::is_pointer_v< Q >, T > operator->()
Definition: soa.h:146
soa::Ptr< T[N]>::operator[]
Ptr< const T > operator[](uint8_t Idx) const
Definition: soa.h:282
soa::Ptr< T[N]>::operator[]
Ptr< T > operator[](uint8_t Idx)
Definition: soa.h:283
soa::BasePtr::operator/=
Ptr< T > & operator/=(const U &Right)
Definition: soa.h:185
soa::BasePtr::operator|=
Ptr< T > & operator|=(const U &Right)
Definition: soa.h:209
soa::Array::Array
constexpr Array()=default
soa::ArrayConstIterator::ArrayConstIterator
ArrayConstIterator(const Array< T, N > &A, uint8_t Idx)
Definition: soa.h:293
soa::BasePtr::operator=
std::enable_if_t<!std::is_const_v< Q >, const Ptr< T > & > operator=(const T &Val)
Definition: soa.h:131
soa::BasePtr::operator<<=
Ptr< T > & operator<<=(const U &Right)
Definition: soa.h:215
soa::BasePtr::operator->
std::enable_if_t<!std::is_pointer_v< Q >, ConstWrapper > operator->() const
Definition: soa.h:162
soa::BasePtr::operator%=
Ptr< T > & operator%=(const U &Right)
Definition: soa.h:191
soa::Array::end
constexpr ArrayIterator< T, N > end()
Definition: soa.h:384
soa::ArrayConstIterator::operator++
ArrayConstIterator & operator++()
Definition: soa.h:299
soa::Array::operator[]
constexpr Ptr< const T > operator[](uint8_t Idx) const
Definition: soa.h:370
soa::Array::size
constexpr uint8_t size() const
Definition: soa.h:388
soa::ArrayIterator::operator*
Ptr< T > operator*() const
Definition: soa.h:323
soa::BasePtr::operator--
T operator--(int)
Definition: soa.h:241
soa::BasePtr::operator&=
Ptr< T > & operator&=(const U &Right)
Definition: soa.h:203
std::array
Definition: array:10
soa::ArrayConstIterator::operator!=
bool operator!=(const ArrayConstIterator &Other) const
Definition: soa.h:307
soa::BasePtr::BasePtr
constexpr BasePtr(uint8_t ByteArrays[][N], uint8_t Idx)
Definition: soa.h:103
soa::BasePtr::operator++
Ptr< T > & operator++()
Definition: soa.h:226
std::initializer_list
Definition: initializer_list:8
soa::BasePtr::operator++
T operator++(int)
Definition: soa.h:235
soa::BasePtr::BytePtrs
const uint8_t * BytePtrs[sizeof(T)]
Definition: soa.h:81
soa::Ptr< T[N]>::Ptr
constexpr Ptr(const uint8_t ByteArrays[][M], uint8_t Idx)
Definition: soa.h:268
soa::BasePtr::operator-=
Ptr< T > & operator-=(const U &Right)
Definition: soa.h:173