OpenShot Audio Library | OpenShotAudio  0.6.0
juce_ArrayBase.h
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
39 template <class ElementType, class TypeOfCriticalSectionToUse>
40 class ArrayBase : public TypeOfCriticalSectionToUse
41 {
42 private:
43  using ParameterType = typename TypeHelpers::ParameterType<ElementType>::type;
44 
45  template <class OtherElementType, class OtherCriticalSection>
46  using AllowConversion = std::enable_if_t<! std::is_same_v<std::tuple<ElementType, TypeOfCriticalSectionToUse>,
47  std::tuple<OtherElementType, OtherCriticalSection>>>;
48 
49 public:
50  //==============================================================================
51  ArrayBase() = default;
52 
53  ~ArrayBase()
54  {
55  clear();
56  }
57 
58  ArrayBase (ArrayBase&& other) noexcept
59  : elements (std::move (other.elements)),
60  numAllocated (other.numAllocated),
61  numUsed (other.numUsed)
62  {
63  other.numAllocated = 0;
64  other.numUsed = 0;
65  }
66 
67  ArrayBase& operator= (ArrayBase&& other) noexcept
68  {
69  if (this != &other)
70  {
71  auto tmp (std::move (other));
72  swapWith (tmp);
73  }
74 
75  return *this;
76  }
77 
83  template <class OtherElementType,
84  class OtherCriticalSection,
85  typename = AllowConversion<OtherElementType, OtherCriticalSection>>
87  : elements (std::move (other.elements)),
88  numAllocated (other.numAllocated),
89  numUsed (other.numUsed)
90  {
91  other.numAllocated = 0;
92  other.numUsed = 0;
93  }
94 
100  template <class OtherElementType,
101  class OtherCriticalSection,
102  typename = AllowConversion<OtherElementType, OtherCriticalSection>>
104  {
105  // No need to worry about assignment to *this, because 'other' must be of a different type.
106  elements = std::move (other.elements);
107  numAllocated = other.numAllocated;
108  numUsed = other.numUsed;
109 
110  other.numAllocated = 0;
111  other.numUsed = 0;
112 
113  return *this;
114  }
115 
116  //==============================================================================
117  template <class OtherArrayType>
118  bool operator== (const OtherArrayType& other) const noexcept
119  {
120  if (size() != (int) other.size())
121  return false;
122 
123  auto* e = begin();
124 
125  for (auto& o : other)
126  if (! exactlyEqual (*e++, o))
127  return false;
128 
129  return true;
130  }
131 
132  template <class OtherArrayType>
133  bool operator!= (const OtherArrayType& other) const noexcept
134  {
135  return ! operator== (other);
136  }
137 
138  //==============================================================================
139  inline ElementType& operator[] (const int index) noexcept
140  {
141  jassert (elements != nullptr);
142  jassert (isPositiveAndBelow (index, numUsed));
143  return elements[index];
144  }
145 
146  inline const ElementType& operator[] (const int index) const noexcept
147  {
148  jassert (elements != nullptr);
149  jassert (isPositiveAndBelow (index, numUsed));
150  return elements[index];
151  }
152 
153  inline ElementType getValueWithDefault (const int index) const noexcept
154  {
155  return isPositiveAndBelow (index, numUsed) ? elements[index] : ElementType();
156  }
157 
158  inline ElementType getFirst() const noexcept
159  {
160  return numUsed > 0 ? elements[0] : ElementType();
161  }
162 
163  inline ElementType getLast() const noexcept
164  {
165  return numUsed > 0 ? elements[numUsed - 1] : ElementType();
166  }
167 
168  //==============================================================================
169  inline ElementType* begin() noexcept
170  {
171  return elements;
172  }
173 
174  inline const ElementType* begin() const noexcept
175  {
176  return elements;
177  }
178 
179  inline ElementType* end() noexcept
180  {
181  return elements + numUsed;
182  }
183 
184  inline const ElementType* end() const noexcept
185  {
186  return elements + numUsed;
187  }
188 
189  inline ElementType* data() noexcept
190  {
191  return elements;
192  }
193 
194  inline const ElementType* data() const noexcept
195  {
196  return elements;
197  }
198 
199  inline int size() const noexcept
200  {
201  return numUsed;
202  }
203 
204  inline int capacity() const noexcept
205  {
206  return numAllocated;
207  }
208 
209  //==============================================================================
210  void setAllocatedSize (int numElements)
211  {
212  jassert (numElements >= numUsed);
213 
214  if (numAllocated != numElements)
215  {
216  if (numElements > 0)
217  setAllocatedSizeInternal (numElements);
218  else
219  elements.free();
220  }
221 
222  numAllocated = numElements;
223  }
224 
225  void ensureAllocatedSize (int minNumElements)
226  {
227  if (minNumElements > numAllocated)
228  setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);
229 
230  jassert (numAllocated <= 0 || elements != nullptr);
231  }
232 
233  void shrinkToNoMoreThan (int maxNumElements)
234  {
235  if (maxNumElements < numAllocated)
236  setAllocatedSize (maxNumElements);
237  }
238 
239  void clear()
240  {
241  for (int i = 0; i < numUsed; ++i)
242  elements[i].~ElementType();
243 
244  numUsed = 0;
245  }
246 
247  //==============================================================================
248  void swapWith (ArrayBase& other) noexcept
249  {
250  elements.swapWith (other.elements);
251  std::swap (numAllocated, other.numAllocated);
252  std::swap (numUsed, other.numUsed);
253  }
254 
255  //==============================================================================
256  void add (const ElementType& newElement)
257  {
258  addImpl (newElement);
259  }
260 
261  void add (ElementType&& newElement)
262  {
263  addImpl (std::move (newElement));
264  }
265 
266  template <typename... OtherElements>
267  void add (const ElementType& firstNewElement, OtherElements&&... otherElements)
268  {
269  addImpl (firstNewElement, std::forward<OtherElements> (otherElements)...);
270  }
271 
272  template <typename... OtherElements>
273  void add (ElementType&& firstNewElement, OtherElements&&... otherElements)
274  {
275  addImpl (std::move (firstNewElement), std::forward<OtherElements> (otherElements)...);
276  }
277 
278  //==============================================================================
279  template <typename Type>
280  void addArray (const Type* elementsToAdd, int numElementsToAdd)
281  {
282  ensureAllocatedSize (numUsed + numElementsToAdd);
283  addArrayInternal (elementsToAdd, numElementsToAdd);
284  numUsed += numElementsToAdd;
285  }
286 
287  template <typename TypeToCreateFrom>
288  void addArray (const std::initializer_list<TypeToCreateFrom>& items)
289  {
290  ensureAllocatedSize (numUsed + (int) items.size());
291 
292  for (auto& item : items)
293  new (elements + numUsed++) ElementType (item);
294  }
295 
296  template <class OtherArrayType>
297  void addArray (const OtherArrayType& arrayToAddFrom)
298  {
299  jassert ((const void*) this != (const void*) &arrayToAddFrom); // can't add from our own elements!
300  ensureAllocatedSize (numUsed + (int) arrayToAddFrom.size());
301 
302  for (auto& e : arrayToAddFrom)
303  addAssumingCapacityIsReady (e);
304  }
305 
306  template <class OtherArrayType>
307  std::enable_if_t<! std::is_pointer_v<OtherArrayType>, int>
308  addArray (const OtherArrayType& arrayToAddFrom,
309  int startIndex, int numElementsToAdd = -1)
310  {
311  jassert ((const void*) this != (const void*) &arrayToAddFrom); // can't add from our own elements!
312 
313  if (startIndex < 0)
314  {
315  jassertfalse;
316  startIndex = 0;
317  }
318 
319  if (numElementsToAdd < 0 || startIndex + numElementsToAdd > (int) arrayToAddFrom.size())
320  numElementsToAdd = (int) arrayToAddFrom.size() - startIndex;
321 
322  addArray (arrayToAddFrom.data() + startIndex, numElementsToAdd);
323 
324  return numElementsToAdd;
325  }
326 
327  //==============================================================================
328  void insert (int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt)
329  {
330  checkSourceIsNotAMember (newElement);
331  auto* space = createInsertSpace (indexToInsertAt, numberOfTimesToInsertIt);
332 
333  for (int i = 0; i < numberOfTimesToInsertIt; ++i)
334  new (space++) ElementType (newElement);
335 
336  numUsed += numberOfTimesToInsertIt;
337  }
338 
339  void insertArray (int indexToInsertAt, const ElementType* newElements, int numberOfElements)
340  {
341  auto* space = createInsertSpace (indexToInsertAt, numberOfElements);
342 
343  for (int i = 0; i < numberOfElements; ++i)
344  new (space++) ElementType (*(newElements++));
345 
346  numUsed += numberOfElements;
347  }
348 
349  //==============================================================================
350  void removeElements (int indexToRemoveAt, int numElementsToRemove)
351  {
352  jassert (indexToRemoveAt >= 0);
353  jassert (numElementsToRemove >= 0);
354  jassert (indexToRemoveAt + numElementsToRemove <= numUsed);
355 
356  if (numElementsToRemove > 0)
357  {
358  removeElementsInternal (indexToRemoveAt, numElementsToRemove);
359  numUsed -= numElementsToRemove;
360  }
361  }
362 
363  //==============================================================================
364  void swap (int index1, int index2)
365  {
366  if (isPositiveAndBelow (index1, numUsed)
367  && isPositiveAndBelow (index2, numUsed))
368  {
369  std::swap (elements[index1],
370  elements[index2]);
371  }
372  }
373 
374  //==============================================================================
375  void move (int currentIndex, int newIndex) noexcept
376  {
377  if (isPositiveAndBelow (currentIndex, numUsed))
378  {
379  if (! isPositiveAndBelow (newIndex, numUsed))
380  newIndex = numUsed - 1;
381 
382  moveInternal (currentIndex, newIndex);
383  }
384  }
385 
386 private:
387  //==============================================================================
388  #if defined (__GNUC__) && __GNUC__ < 5 && ! defined (__clang__)
389  static constexpr auto isTriviallyCopyable = std::is_scalar_v<ElementType>;
390  #else
391  static constexpr auto isTriviallyCopyable = std::is_trivially_copyable_v<ElementType>;
392  #endif
393 
394  //==============================================================================
395  template <typename Type>
396  void addArrayInternal (const Type* otherElements, int numElements)
397  {
398  if constexpr (isTriviallyCopyable && std::is_same_v<Type, ElementType>)
399  {
400  if (numElements > 0)
401  memcpy (elements + numUsed, otherElements, (size_t) numElements * sizeof (ElementType));
402  }
403  else
404  {
405  auto* start = elements + numUsed;
406 
407  while (--numElements >= 0)
408  new (start++) ElementType (*(otherElements++));
409  }
410  }
411 
412  //==============================================================================
413  void setAllocatedSizeInternal (int numElements)
414  {
415  if constexpr (isTriviallyCopyable)
416  {
417  elements.realloc ((size_t) numElements);
418  }
419  else
420  {
421  HeapBlock<ElementType> newElements (numElements);
422 
423  for (int i = 0; i < numUsed; ++i)
424  {
425  new (newElements + i) ElementType (std::move (elements[i]));
426  elements[i].~ElementType();
427  }
428 
429  elements = std::move (newElements);
430  }
431  }
432 
433  //==============================================================================
434  ElementType* createInsertSpace (int indexToInsertAt, int numElements)
435  {
436  ensureAllocatedSize (numUsed + numElements);
437 
438  if (! isPositiveAndBelow (indexToInsertAt, numUsed))
439  return elements + numUsed;
440 
441  createInsertSpaceInternal (indexToInsertAt, numElements);
442 
443  return elements + indexToInsertAt;
444  }
445 
446  void createInsertSpaceInternal (int indexToInsertAt, int numElements)
447  {
448  if constexpr (isTriviallyCopyable)
449  {
450  auto* start = elements + indexToInsertAt;
451  auto numElementsToShift = numUsed - indexToInsertAt;
452  memmove (start + numElements, start, (size_t) numElementsToShift * sizeof (ElementType));
453  }
454  else
455  {
456  auto* end = elements + numUsed;
457  auto* newEnd = end + numElements;
458  auto numElementsToShift = numUsed - indexToInsertAt;
459 
460  for (int i = 0; i < numElementsToShift; ++i)
461  {
462  new (--newEnd) ElementType (std::move (*(--end)));
463  end->~ElementType();
464  }
465  }
466  }
467 
468  //==============================================================================
469  void removeElementsInternal (int indexToRemoveAt, int numElementsToRemove)
470  {
471  if constexpr (isTriviallyCopyable)
472  {
473  auto* start = elements + indexToRemoveAt;
474  auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove);
475  memmove (start, start + numElementsToRemove, (size_t) numElementsToShift * sizeof (ElementType));
476  }
477  else
478  {
479  auto numElementsToShift = numUsed - (indexToRemoveAt + numElementsToRemove);
480  auto* destination = elements + indexToRemoveAt;
481  auto* source = destination + numElementsToRemove;
482 
483  for (int i = 0; i < numElementsToShift; ++i)
484  moveAssignElement (destination++, std::move (*(source++)));
485 
486  for (int i = 0; i < numElementsToRemove; ++i)
487  (destination++)->~ElementType();
488  }
489  }
490 
491  //==============================================================================
492  void moveInternal (int currentIndex, int newIndex) noexcept
493  {
494  if constexpr (isTriviallyCopyable)
495  {
496  char tempCopy[sizeof (ElementType)];
497  memcpy (tempCopy, elements + currentIndex, sizeof (ElementType));
498 
499  if (newIndex > currentIndex)
500  {
501  memmove (elements + currentIndex,
502  elements + currentIndex + 1,
503  (size_t) (newIndex - currentIndex) * sizeof (ElementType));
504  }
505  else
506  {
507  memmove (elements + newIndex + 1,
508  elements + newIndex,
509  (size_t) (currentIndex - newIndex) * sizeof (ElementType));
510  }
511 
512  memcpy (elements + newIndex, tempCopy, sizeof (ElementType));
513  }
514  else
515  {
516  auto* e = elements + currentIndex;
517  ElementType tempCopy (std::move (*e));
518  auto delta = newIndex - currentIndex;
519 
520  if (delta > 0)
521  {
522  for (int i = 0; i < delta; ++i)
523  {
524  moveAssignElement (e, std::move (*(e + 1)));
525  ++e;
526  }
527  }
528  else
529  {
530  for (int i = 0; i < -delta; ++i)
531  {
532  moveAssignElement (e, std::move (*(e - 1)));
533  --e;
534  }
535  }
536 
537  moveAssignElement (e, std::move (tempCopy));
538  }
539  }
540 
541  //==============================================================================
542  template <typename... Elements>
543  void addImpl (Elements&&... toAdd)
544  {
545  (checkSourceIsNotAMember (toAdd), ...);
546  ensureAllocatedSize (numUsed + (int) sizeof... (toAdd));
547  addAssumingCapacityIsReady (std::forward<Elements> (toAdd)...);
548  }
549 
550  template <typename... Elements>
551  void addAssumingCapacityIsReady (Elements&&... toAdd)
552  {
553  (new (elements + numUsed++) ElementType (std::forward<Elements> (toAdd)), ...);
554  }
555 
556  //==============================================================================
557  void moveAssignElement (ElementType* destination, ElementType&& source)
558  {
559  if constexpr (std::is_move_assignable_v<ElementType>)
560  {
561  *destination = std::move (source);
562  }
563  else
564  {
565  destination->~ElementType();
566  new (destination) ElementType (std::move (source));
567  }
568  }
569 
570  void checkSourceIsNotAMember ([[maybe_unused]] const ElementType& element)
571  {
572  // when you pass a reference to an existing element into a method like add() which
573  // may need to reallocate the array to make more space, the incoming reference may
574  // be deleted indirectly during the reallocation operation! To work around this,
575  // make a local copy of the item you're trying to add (and maybe use std::move to
576  // move it into the add() method to avoid any extra overhead)
577  jassert (std::addressof (element) < begin() || end() <= std::addressof (element));
578  }
579 
580  //==============================================================================
581  HeapBlock<ElementType> elements;
582  int numAllocated = 0, numUsed = 0;
583 
584  template <class OtherElementType, class OtherCriticalSection>
585  friend class ArrayBase;
586 
587  JUCE_DECLARE_NON_COPYABLE (ArrayBase)
588 };
589 
590 } // namespace juce
ArrayBase(ArrayBase< OtherElementType, OtherCriticalSection > &&other) noexcept