SPH
Array.h
Go to the documentation of this file.
1 #pragma once
2 
7 
8 #include "math/MathBasic.h"
11 
12 #ifdef SPH_GCC
13 #pragma GCC diagnostic ignored "-Wstringop-overflow"
14 #endif
15 
17 
18 template <typename T, typename TAllocator = Mallocator, typename TCounter = Size>
19 class CopyableArray;
20 
22 template <typename TValue>
24 
25 template <>
26 struct NumericLimits<uint64_t> {
27  static constexpr uint64_t max() {
28  return uint64_t(-1);
29  }
30 };
31 
32 template <>
33 struct NumericLimits<uint32_t> {
34  static constexpr uint32_t max() {
35  return uint32_t(-1);
36  }
37 };
38 
42 template <typename T, typename TAllocator = Mallocator, typename TCounter = Size>
43 class Array : private TAllocator, public Noncopyable {
44 private:
45  using StorageType = typename WrapReferenceType<T>::Type;
46  StorageType* data = nullptr;
47  TCounter actSize = 0;
48  TCounter maxSize = 0;
49 
50  static constexpr TCounter maxValue = NumericLimits<TCounter>::max();
51 
52 public:
53  using Type = T;
54  using Counter = TCounter;
55 
56  Array() = default;
57 
62  explicit Array(const TCounter elementCnt, const TCounter allocatedSize = maxValue) {
63  this->alloc(elementCnt, allocatedSize);
64 
65  // emplace elements
66  if (!std::is_trivially_default_constructible<T>::value) {
67  for (TCounter i = 0; i < actSize; ++i) {
68  new (data + i) StorageType();
69  }
70  }
71  }
72 
77  Array(std::initializer_list<StorageType> list) {
78  actSize = list.size();
79  maxSize = actSize;
80  MemoryBlock block = TAllocator::allocate(maxSize * sizeof(StorageType), alignof(StorageType));
81  SPH_ASSERT(block.ptr);
82  data = (StorageType*)block.ptr;
83  TCounter i = 0;
84  for (auto& l : list) {
85  new (data + i) StorageType(l);
86  i++;
87  }
88  }
89 
91  Array(Array&& other) {
92  std::swap(data, other.data);
93  std::swap(maxSize, other.maxSize);
94  std::swap(actSize, other.actSize);
95  }
96 
97  ~Array() {
98  // explicitly destroy all constructed elements
99  if (!std::is_trivially_destructible<T>::value) {
100  for (TCounter i = 0; i < actSize; ++i) {
101  data[i].~StorageType();
102  }
103  }
104  if (data) {
105  MemoryBlock block(data, maxSize * sizeof(StorageType));
106  TAllocator::deallocate(block);
107  data = nullptr;
108  }
109  }
110 
111  Array& operator=(Array&& other) {
112  std::swap(data, other.data);
113  std::swap(maxSize, other.maxSize);
114  std::swap(actSize, other.actSize);
115  return *this;
116  }
117 
124  const Array& rhs = other;
125  this->resize(rhs.size());
126  for (TCounter i = 0; i < rhs.size(); ++i) {
127  data[i] = rhs[i];
128  }
129  return *this;
130  }
131 
135  template <typename U, typename = std::enable_if_t<std::is_lvalue_reference<T>::value, U>>
137  SPH_ASSERT(this->size() == other.size());
138  for (TCounter i = 0; i < other.size(); ++i) {
139  (*this)[i] = std::forward<U>(other[i]);
140  }
141  return *this;
142  }
143 
147  Array clone() const {
148  Array newArray;
149  newArray.reserve(actSize);
150  for (const T& value : *this) {
151  newArray.emplaceBack(value);
152  }
153  return newArray;
154  }
155 
156  INLINE T& operator[](const TCounter idx) noexcept {
157  SPH_ASSERT(unsigned(idx) < unsigned(actSize), idx, actSize);
158  return data[idx];
159  }
160 
161  INLINE const T& operator[](const TCounter idx) const noexcept {
162  SPH_ASSERT(unsigned(idx) < unsigned(actSize), idx, actSize);
163  return data[idx];
164  }
165 
166  INLINE T& front() noexcept {
167  SPH_ASSERT(actSize > 0);
168  return data[0];
169  }
170 
171  INLINE const T& front() const noexcept {
172  SPH_ASSERT(actSize > 0);
173  return data[0];
174  }
175 
176  INLINE T& back() noexcept {
177  SPH_ASSERT(actSize > 0);
178  return data[actSize - 1];
179  }
180 
181  INLINE const T& back() const noexcept {
182  SPH_ASSERT(actSize > 0);
183  return data[actSize - 1];
184  }
185 
187  void fill(const T& t) {
188  for (auto& v : *this) {
189  v = t;
190  }
191  }
192 
193  INLINE TCounter size() const noexcept {
194  return actSize;
195  }
196 
197  INLINE TCounter capacity() const noexcept {
198  return maxSize;
199  }
200 
201  INLINE bool empty() const noexcept {
202  return actSize == 0;
203  }
204 
215  void resize(const TCounter newSize) {
216  // check suspiciously high values
217  SPH_ASSERT(newSize < (NumericLimits<TCounter>::max() >> 1));
218  if (newSize <= maxSize) {
219  // enough elements is already allocated
220  if (newSize >= actSize) {
221  // enlarging array, we need to construct some new elements
222  if (!std::is_trivially_default_constructible<T>::value) {
223  for (TCounter i = actSize; i < newSize; ++i) {
224  new (data + i) StorageType();
225  }
226  }
227  } else {
228  if (!std::is_trivially_destructible<T>::value) {
229  // shrinking array, we need to delete some elements
230  for (TCounter i = newSize; i < actSize; ++i) {
231  data[i].~StorageType();
232  }
233  }
234  }
235  } else {
236  // requested more elements than allocate, need to reallocated.
237  // allocate twice the current number or the new size, whatever is higher, to avoid frequent
238  // reallocation when pushing elements one by one
239  const TCounter actNewSize = max(2 * maxSize, newSize);
240  Array newArray(0, actNewSize);
241  // copy all elements into the new array, using move constructor
242  for (TCounter i = 0; i < actSize; ++i) {
243  new (newArray.data + i) StorageType(std::move(data[i]));
244  }
245  // default-construct new elements
246  if (!std::is_trivially_default_constructible<T>::value) {
247  for (TCounter i = actSize; i < newSize; ++i) {
248  new (newArray.data + i) StorageType();
249  }
250  }
251  // move the array into this
252  *this = std::move(newArray);
253  }
254  actSize = newSize;
255  }
256 
265  void resizeAndSet(const TCounter newSize, const T& value) {
266  const TCounter currentSize = actSize;
267  this->resize(newSize);
268  for (TCounter i = currentSize; i < actSize; ++i) {
269  (*this)[i] = value;
270  }
271  }
272 
279  void reserve(const TCounter newMaxSize) {
280  SPH_ASSERT(newMaxSize < (NumericLimits<TCounter>::max() >> 1));
281  if (newMaxSize > maxSize) {
282  const TCounter actNewSize = max(2 * maxSize, newMaxSize);
283  Array newArray;
284  // don't use the Array(0, actNewSize) constructor to allow using emplaceBack for types without
285  // default constructor
286  newArray.alloc(0, actNewSize);
287  // copy all elements into the new array, using move constructor
288  for (TCounter i = 0; i < actSize; ++i) {
289  new (newArray.data + i) StorageType(std::move(data[i]));
290  }
291  newArray.actSize = actSize;
292  // move the array into this
293  *this = std::move(newArray);
294  }
295  }
296 
298  void shrink() {
299  Array newArray;
300  newArray.pushAll(std::move(*this));
301  *this = std::move(newArray);
302  }
303 
305  template <typename U>
306  INLINE void push(U&& u) {
307  resize(actSize + 1);
308  data[actSize - 1] = std::forward<U>(u);
309  }
310 
311  template <typename TIter>
312  void pushAll(const TIter first, const TIter last) {
313  for (TIter iter = first; iter != last; ++iter) {
314  push(*iter);
315  }
316  }
317 
318  void pushAll(const Array& other) {
319  reserve(actSize + other.size());
320  pushAll(other.cbegin(), other.cend());
321  }
322 
323  void pushAll(Array&& other) {
324  reserve(actSize + other.size());
325  for (T& value : other) {
326  push(std::move(value));
327  }
328  }
329 
331  template <typename... TArgs>
332  StorageType& emplaceBack(TArgs&&... args) {
333  reserve(actSize + 1);
334  SPH_ASSERT(maxSize > actSize);
335  StorageType* ptr = new (data + actSize) StorageType(std::forward<TArgs>(args)...);
336  SPH_ASSERT(ptr);
337  actSize++;
338  return *ptr;
339  }
340 
344  template <typename U>
345  void insert(const TCounter position, U&& value) {
346  SPH_ASSERT(position <= actSize);
347  this->resize(actSize + 1);
348  std::move_backward(this->begin() + position, this->end() - 1, this->end());
349  data[position] = std::forward<U>(value);
350  }
351 
356  template <typename TIterator>
357  void insert(const TCounter position, const TIterator first, const TIterator last) {
358  SPH_ASSERT(position <= actSize);
359  if (SPH_UNLIKELY(first == last)) {
360  // inserting an empty range
361  return;
362  }
363  const Size count = std::distance(first, last);
364  this->resize(actSize + count);
365  std::move_backward(this->begin() + position, this->end() - count, this->end());
366  Size i = position;
367  for (TIterator iter = first; iter != last; ++iter) {
368  data[i++] = *iter;
369  }
370  }
371 
375  INLINE T pop() {
376  SPH_ASSERT(actSize > 0);
377  T value = data[actSize - 1];
378  resize(actSize - 1);
379  return value;
380  }
381 
383  void remove(const TCounter idx) {
384  SPH_ASSERT(idx < actSize);
385  for (TCounter i = idx; i < actSize - 1; ++i) {
386  data[i] = std::move(data[i + 1]);
387  }
388  resize(actSize - 1);
389  }
390 
396  Size shift = 0;
397  if (SPH_UNLIKELY(idxs.empty())) {
398  return;
399  }
400 
401  // move all elements between indices
402  for (Size k = 0; k < idxs.size() - 1; ++k) {
403  SPH_ASSERT(idxs[k] < idxs[k + 1]);
404  for (TCounter i = idxs[k]; i < idxs[k + 1] - 1; ++i) {
405  data[i - shift] = std::move(data[i + 1]);
406  }
407  shift++;
408  }
409  // move all elements after last index
410  for (TCounter i = idxs.back(); i < actSize - 1; ++i) {
411  data[i - shift] = std::move(data[i + 1]);
412  }
413 
414  resize(actSize - idxs.size());
415  }
416 
418  template <typename TIter>
419  void remove(TIter first, TIter last) {
420  SPH_ASSERT(first <= last);
421  if (SPH_UNLIKELY(first == last)) {
422  return;
423  }
424  const Size count = last - first;
425  SPH_ASSERT(Size(first - begin()) + count <= actSize);
426 
427  for (TIter iter = first; iter != end() - count; ++iter) {
428  *iter = std::move(*(iter + count));
429  }
430  resize(actSize - count);
431  }
432 
434  void clear() {
435  if (!std::is_trivially_destructible<T>::value) {
436  for (TCounter i = 0; i < actSize; ++i) {
437  data[i].~StorageType();
438  }
439  }
440  actSize = 0;
441  }
442 
444  void swap(Array& other) {
445  std::swap(data, other.data);
446  std::swap(maxSize, other.maxSize);
447  std::swap(actSize, other.actSize);
448  }
449 
451  return Iterator<StorageType>(data, data, data + actSize);
452  }
453 
455  return Iterator<const StorageType>(data, data, data + actSize);
456  }
457 
459  return Iterator<const StorageType>(data, data, data + actSize);
460  }
461 
463  return Iterator<StorageType>(data + actSize, data, data + actSize);
464  }
465 
467  return Iterator<const StorageType>(data + actSize, data, data + actSize);
468  }
469 
471  return Iterator<const StorageType>(data + actSize, data, data + actSize);
472  }
473 
475  const TAllocator& allocator() const {
476  return *this;
477  }
478 
480  TAllocator& allocator() {
481  return *this;
482  }
483 
485  INLINE operator ArrayView<T, TCounter>() noexcept {
486  return ArrayView<T, TCounter>(data, actSize);
487  }
488 
490  INLINE operator ArrayView<const T, TCounter>() const noexcept {
491  return ArrayView<const T, TCounter>(data, actSize);
492  }
493 
496  return ArrayView<T, TCounter>(data, actSize);
497  }
498 
501  return ArrayView<const T, TCounter>(data, actSize);
502  }
503 
508  bool operator==(const Array& other) const noexcept {
509  return view() == other.view();
510  }
511 
513  bool operator!=(const Array& other) const noexcept {
514  return view() != other.view();
515  }
516 
520  template <typename TStream, typename = std::enable_if_t<HasStreamOperator<T, TStream>::value>>
521  friend TStream& operator<<(TStream& stream, const Array& array) {
522  for (const T& t : array) {
523  stream << t << std::endl;
524  }
525  return stream;
526  }
527 
528 private:
529  void alloc(const TCounter elementCnt, const TCounter allocatedSize) {
530  actSize = elementCnt;
531  maxSize = allocatedSize;
532  if (allocatedSize == maxValue) {
533  maxSize = actSize;
534  }
535  // allocate maxSize elements
536  MemoryBlock block = TAllocator::allocate(maxSize * sizeof(StorageType), alignof(StorageType));
537  SPH_ASSERT(block.ptr);
538  data = (StorageType*)block.ptr;
539  }
540 };
541 
542 template <typename T, typename TAllocator, typename TCounter>
544 private:
545  const Array<T, TAllocator, TCounter>& array;
546 
547 public:
549  : array(array) {}
550 
551  operator const Array<T, TAllocator, TCounter>&() const {
552  return array;
553  }
554 };
555 
557 template <typename T, typename TAllocator, typename TCounter>
560 }
561 
563 template <typename T0, typename... TArgs>
564 Array<std::decay_t<T0>> makeArray(T0&& t0, TArgs&&... rest) {
565  return Array<std::decay_t<T0>>{ std::forward<T0>(t0), std::forward<TArgs>(rest)... };
566 }
567 
569 template <typename T0, typename... TArgs>
570 Array<T0&> tieToArray(T0& t0, TArgs&... rest) {
571  return Array<T0&>{ t0, rest... };
572 }
573 
574 
576 
578 namespace std {
579 template <typename T, typename TCounter>
580 void swap(Sph::Array<T, TCounter>& ar1, Sph::Array<T, TCounter>& ar2) {
581  ar1.swap(ar2);
582 }
583 } // namespace std
Allocators used by containers.
Simple non-owning view of a container.
Array< std::decay_t< T0 > > makeArray(T0 &&t0, TArgs &&... rest)
Creates an array from a list of parameters.
Definition: Array.h:564
Array< T0 & > tieToArray(T0 &t0, TArgs &... rest)
Creates a l-value reference array from a list of l-value references.
Definition: Array.h:570
INLINE CopyableArray< T, TAllocator, TCounter > copyable(const Array< T, TAllocator, TCounter > &array)
Definition: Array.h:558
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
Basic math routines.
constexpr INLINE T max(const T &f1, const T &f2)
Definition: MathBasic.h:20
#define INLINE
Macros for conditional compilation based on selected compiler.
Definition: Object.h:31
#define SPH_UNLIKELY(x)
Definition: Object.h:50
#define NAMESPACE_SPH_END
Definition: Object.h:12
INLINE Float distance(const Vector &r, const Vector &axis)
Returns the distance of vector from given axis. The axis is assumed to be normalized.
Definition: Vector.h:827
Object providing safe access to continuous memory of data.
Definition: ArrayView.h:17
INLINE bool empty() const
Definition: ArrayView.h:105
INLINE TCounter size() const
Definition: ArrayView.h:101
INLINE T & back()
Definition: ArrayView.h:91
Generic dynamically allocated resizable storage.
Definition: Array.h:43
bool operator==(const Array &other) const noexcept
Comparison operator, comparings array element-by-element.
Definition: Array.h:508
INLINE Iterator< const StorageType > cbegin() const noexcept
Definition: Array.h:458
INLINE const T & front() const noexcept
Definition: Array.h:171
INLINE const T & operator[](const TCounter idx) const noexcept
Definition: Array.h:161
void resize(const TCounter newSize)
Resizes the array to new size.
Definition: Array.h:215
void pushAll(Array &&other)
Definition: Array.h:323
void reserve(const TCounter newMaxSize)
Allocates enough memory to store the given number of elements.
Definition: Array.h:279
TCounter Counter
Definition: Array.h:54
INLINE Iterator< StorageType > end() noexcept
Definition: Array.h:462
void remove(const ArrayView< const TCounter > idxs)
Removes elements specified by indices from the array.
Definition: Array.h:395
TAllocator & allocator()
Returns the interface to the allocator.
Definition: Array.h:480
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
StorageType & emplaceBack(TArgs &&... args)
Constructs a new element at the end of the array in place, using the provided arguments.
Definition: Array.h:332
Array(const TCounter elementCnt, const TCounter allocatedSize=maxValue)
Constructs array of given size.
Definition: Array.h:62
void remove(const TCounter idx)
Removes an element with given index from the array.
Definition: Array.h:383
Array & operator=(Array &&other)
Definition: Array.h:111
Array(std::initializer_list< StorageType > list)
Constructs array from initialized list.
Definition: Array.h:77
ArrayView< T, TCounter > view() noexcept
Explicit conversion to arrayview.
Definition: Array.h:495
void fill(const T &t)
Sets all elements of the array to given value.
Definition: Array.h:187
T Type
Definition: Array.h:53
Array & operator=(Array< U > &&other)
For l-value references assign each value (does not actually move anything).
Definition: Array.h:136
void insert(const TCounter position, U &&value)
Inserts a new element to given position in the array.
Definition: Array.h:345
Array()=default
INLINE T & back() noexcept
Definition: Array.h:176
Array & operator=(const CopyableArray< T, TAllocator, TCounter > &other)
Performs deep-copy of array elements, resizing array if needed.
Definition: Array.h:123
void remove(TIter first, TIter last)
Removes all elements in given range.
Definition: Array.h:419
INLINE T pop()
Removes the last element from the array and return its value.
Definition: Array.h:375
ArrayView< const T, TCounter > view() const noexcept
Explicit conversion to arrayview, const version.
Definition: Array.h:500
INLINE TCounter capacity() const noexcept
Definition: Array.h:197
Array(Array &&other)
Move constructor from array of the same type.
Definition: Array.h:91
void clear()
Removes all elements from the array, but does NOT release the memory.
Definition: Array.h:434
INLINE const T & back() const noexcept
Definition: Array.h:181
INLINE TCounter size() const noexcept
Definition: Array.h:193
~Array()
Definition: Array.h:97
void insert(const TCounter position, const TIterator first, const TIterator last)
Inserts a range of values into the array, starting at given position.
Definition: Array.h:357
INLINE T & front() noexcept
Definition: Array.h:166
INLINE Iterator< const StorageType > begin() const noexcept
Definition: Array.h:454
void swap(Array &other)
Swaps content of two arrays.
Definition: Array.h:444
void shrink()
Reallocates the array, removing the unused elements to save memory.
Definition: Array.h:298
INLINE T & operator[](const TCounter idx) noexcept
Definition: Array.h:156
INLINE Iterator< const StorageType > end() const noexcept
Definition: Array.h:466
const TAllocator & allocator() const
Returns the interface to the allocator.
Definition: Array.h:475
INLINE bool empty() const noexcept
Definition: Array.h:201
INLINE Iterator< const StorageType > cend() const noexcept
Definition: Array.h:470
void resizeAndSet(const TCounter newSize, const T &value)
Resizes the array to new size and assigns a given value to all newly created elements.
Definition: Array.h:265
INLINE Iterator< StorageType > begin() noexcept
Definition: Array.h:450
void pushAll(const TIter first, const TIter last)
Definition: Array.h:312
void pushAll(const Array &other)
Definition: Array.h:318
friend TStream & operator<<(TStream &stream, const Array &array)
Prints content of array to stream.
Definition: Array.h:521
bool operator!=(const Array &other) const noexcept
Inequality operator.
Definition: Array.h:513
Array clone() const
Performs a deep copy of all elements of the array.
Definition: Array.h:147
CopyableArray(const Array< T, TAllocator, TCounter > &array)
Definition: Array.h:548
Simple (forward) iterator over continuous array of objects of type T.
Definition: Iterator.h:18
Vector position(const Float a, const Float e, const Float u)
Computes the position on the elliptic trajectory.
Definition: TwoBody.cpp:82
Overload of std::swap for Sph::Array.
Definition: Array.h:578
void swap(Sph::Array< T, TCounter > &ar1, Sph::Array< T, TCounter > &ar2)
Definition: Array.h:580
void * ptr
Definition: Allocators.h:15
Object with deleted copy constructor and copy operator.
Definition: Object.h:54
static constexpr uint32_t max()
Definition: Array.h:34
static constexpr uint64_t max()
Definition: Array.h:27
Helper class, used to avoid including large header limits.h.
Definition: Array.h:23