The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

591 lines
19KB

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