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.

595 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #ifndef __JUCE_SORTEDSET_JUCEHEADER__
  19. #define __JUCE_SORTEDSET_JUCEHEADER__
  20. #include "juce_ArrayAllocationBase.h"
  21. #include "../threads/juce_CriticalSection.h"
  22. #if JUCE_MSVC
  23. #pragma warning (push)
  24. #pragma warning (disable: 4512)
  25. #endif
  26. //==============================================================================
  27. /**
  28. Holds a set of unique primitive objects, such as ints or doubles.
  29. A set can only hold one item with a given value, so if for example it's a
  30. set of integers, attempting to add the same integer twice will do nothing
  31. the second time.
  32. Internally, the list of items is kept sorted (which means that whatever
  33. kind of primitive type is used must support the ==, <, >, <= and >= operators
  34. to determine the order), and searching the set for known values is very fast
  35. because it uses a binary-chop method.
  36. Note that if you're using a class or struct as the element type, it must be
  37. capable of being copied or moved with a straightforward memcpy, rather than
  38. needing construction and destruction code.
  39. To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
  40. TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
  41. @see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
  42. */
  43. template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
  44. class SortedSet
  45. {
  46. public:
  47. //==============================================================================
  48. /** Creates an empty set. */
  49. SortedSet() noexcept
  50. : numUsed (0)
  51. {
  52. }
  53. /** Creates a copy of another set.
  54. @param other the set to copy
  55. */
  56. SortedSet (const SortedSet& other) noexcept
  57. {
  58. const ScopedLockType lock (other.getLock());
  59. numUsed = other.numUsed;
  60. data.setAllocatedSize (other.numUsed);
  61. memcpy (data.elements, other.data.elements, numUsed * sizeof (ElementType));
  62. }
  63. /** Destructor. */
  64. ~SortedSet() noexcept
  65. {
  66. }
  67. /** Copies another set over this one.
  68. @param other the set to copy
  69. */
  70. SortedSet& operator= (const SortedSet& other) noexcept
  71. {
  72. if (this != &other)
  73. {
  74. const ScopedLockType lock1 (other.getLock());
  75. const ScopedLockType lock2 (getLock());
  76. data.ensureAllocatedSize (other.size());
  77. numUsed = other.numUsed;
  78. memcpy (data.elements, other.data.elements, numUsed * sizeof (ElementType));
  79. minimiseStorageOverheads();
  80. }
  81. return *this;
  82. }
  83. //==============================================================================
  84. /** Compares this set to another one.
  85. Two sets are considered equal if they both contain the same set of
  86. elements.
  87. @param other the other set to compare with
  88. */
  89. bool operator== (const SortedSet<ElementType>& other) const noexcept
  90. {
  91. const ScopedLockType lock (getLock());
  92. if (numUsed != other.numUsed)
  93. return false;
  94. for (int i = numUsed; --i >= 0;)
  95. if (! (data.elements[i] == other.data.elements[i]))
  96. return false;
  97. return true;
  98. }
  99. /** Compares this set to another one.
  100. Two sets are considered equal if they both contain the same set of
  101. elements.
  102. @param other the other set to compare with
  103. */
  104. bool operator!= (const SortedSet<ElementType>& other) const noexcept
  105. {
  106. return ! operator== (other);
  107. }
  108. //==============================================================================
  109. /** Removes all elements from the set.
  110. This will remove all the elements, and free any storage that the set is
  111. using. To clear it without freeing the storage, use the clearQuick()
  112. method instead.
  113. @see clearQuick
  114. */
  115. void clear() noexcept
  116. {
  117. const ScopedLockType lock (getLock());
  118. data.setAllocatedSize (0);
  119. numUsed = 0;
  120. }
  121. /** Removes all elements from the set without freeing the array's allocated storage.
  122. @see clear
  123. */
  124. void clearQuick() noexcept
  125. {
  126. const ScopedLockType lock (getLock());
  127. numUsed = 0;
  128. }
  129. //==============================================================================
  130. /** Returns the current number of elements in the set.
  131. */
  132. inline int size() const noexcept
  133. {
  134. return numUsed;
  135. }
  136. /** Returns one of the elements in the set.
  137. If the index passed in is beyond the range of valid elements, this
  138. will return zero.
  139. If you're certain that the index will always be a valid element, you
  140. can call getUnchecked() instead, which is faster.
  141. @param index the index of the element being requested (0 is the first element in the set)
  142. @see getUnchecked, getFirst, getLast
  143. */
  144. inline ElementType operator[] (const int index) const noexcept
  145. {
  146. const ScopedLockType lock (getLock());
  147. return isPositiveAndBelow (index, numUsed) ? data.elements [index]
  148. : ElementType();
  149. }
  150. /** Returns one of the elements in the set, without checking the index passed in.
  151. Unlike the operator[] method, this will try to return an element without
  152. checking that the index is within the bounds of the set, so should only
  153. be used when you're confident that it will always be a valid index.
  154. @param index the index of the element being requested (0 is the first element in the set)
  155. @see operator[], getFirst, getLast
  156. */
  157. inline ElementType getUnchecked (const int index) const noexcept
  158. {
  159. const ScopedLockType lock (getLock());
  160. jassert (isPositiveAndBelow (index, numUsed));
  161. return data.elements [index];
  162. }
  163. /** Returns a direct reference to one of the elements in the set, without checking the index passed in.
  164. This is like getUnchecked, but returns a direct reference to the element, so that
  165. you can alter it directly. Obviously this can be dangerous, so only use it when
  166. absolutely necessary.
  167. @param index the index of the element being requested (0 is the first element in the array)
  168. */
  169. inline ElementType& getReference (const int index) const noexcept
  170. {
  171. const ScopedLockType lock (getLock());
  172. jassert (isPositiveAndBelow (index, numUsed));
  173. return data.elements [index];
  174. }
  175. /** Returns the first element in the set, or 0 if the set is empty.
  176. @see operator[], getUnchecked, getLast
  177. */
  178. inline ElementType getFirst() const noexcept
  179. {
  180. const ScopedLockType lock (getLock());
  181. return numUsed > 0 ? data.elements [0] : ElementType();
  182. }
  183. /** Returns the last element in the set, or 0 if the set is empty.
  184. @see operator[], getUnchecked, getFirst
  185. */
  186. inline ElementType getLast() const noexcept
  187. {
  188. const ScopedLockType lock (getLock());
  189. return numUsed > 0 ? data.elements [numUsed - 1] : ElementType();
  190. }
  191. //==============================================================================
  192. /** Returns a pointer to the first element in the set.
  193. This method is provided for compatibility with standard C++ iteration mechanisms.
  194. */
  195. inline ElementType* begin() const noexcept
  196. {
  197. return data.elements;
  198. }
  199. /** Returns a pointer to the element which follows the last element in the set.
  200. This method is provided for compatibility with standard C++ iteration mechanisms.
  201. */
  202. inline ElementType* end() const noexcept
  203. {
  204. return data.elements + numUsed;
  205. }
  206. //==============================================================================
  207. /** Finds the index of the first element which matches the value passed in.
  208. This will search the set for the given object, and return the index
  209. of its first occurrence. If the object isn't found, the method will return -1.
  210. @param elementToLookFor the value or object to look for
  211. @returns the index of the object, or -1 if it's not found
  212. */
  213. int indexOf (const ElementType elementToLookFor) const noexcept
  214. {
  215. const ScopedLockType lock (getLock());
  216. int start = 0;
  217. int end_ = numUsed;
  218. for (;;)
  219. {
  220. if (start >= end_)
  221. {
  222. return -1;
  223. }
  224. else if (elementToLookFor == data.elements [start])
  225. {
  226. return start;
  227. }
  228. else
  229. {
  230. const int halfway = (start + end_) >> 1;
  231. if (halfway == start)
  232. return -1;
  233. else if (elementToLookFor < data.elements [halfway])
  234. end_ = halfway;
  235. else
  236. start = halfway;
  237. }
  238. }
  239. }
  240. /** Returns true if the set contains at least one occurrence of an object.
  241. @param elementToLookFor the value or object to look for
  242. @returns true if the item is found
  243. */
  244. bool contains (const ElementType elementToLookFor) const noexcept
  245. {
  246. const ScopedLockType lock (getLock());
  247. int start = 0;
  248. int end_ = numUsed;
  249. for (;;)
  250. {
  251. if (start >= end_)
  252. {
  253. return false;
  254. }
  255. else if (elementToLookFor == data.elements [start])
  256. {
  257. return true;
  258. }
  259. else
  260. {
  261. const int halfway = (start + end_) >> 1;
  262. if (halfway == start)
  263. return false;
  264. else if (elementToLookFor < data.elements [halfway])
  265. end_ = halfway;
  266. else
  267. start = halfway;
  268. }
  269. }
  270. }
  271. //==============================================================================
  272. /** Adds a new element to the set, (as long as it's not already in there).
  273. @param newElement the new object to add to the set
  274. @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
  275. */
  276. void add (const ElementType newElement) noexcept
  277. {
  278. const ScopedLockType lock (getLock());
  279. int start = 0;
  280. int end_ = numUsed;
  281. for (;;)
  282. {
  283. if (start >= end_)
  284. {
  285. jassert (start <= end_);
  286. insertInternal (start, newElement);
  287. break;
  288. }
  289. else if (newElement == data.elements [start])
  290. {
  291. break;
  292. }
  293. else
  294. {
  295. const int halfway = (start + end_) >> 1;
  296. if (halfway == start)
  297. {
  298. if (newElement < data.elements [halfway])
  299. insertInternal (start, newElement);
  300. else
  301. insertInternal (start + 1, newElement);
  302. break;
  303. }
  304. else if (newElement < data.elements [halfway])
  305. end_ = halfway;
  306. else
  307. start = halfway;
  308. }
  309. }
  310. }
  311. /** Adds elements from an array to this set.
  312. @param elementsToAdd the array of elements to add
  313. @param numElementsToAdd how many elements are in this other array
  314. @see add
  315. */
  316. void addArray (const ElementType* elementsToAdd,
  317. int numElementsToAdd) noexcept
  318. {
  319. const ScopedLockType lock (getLock());
  320. while (--numElementsToAdd >= 0)
  321. add (*elementsToAdd++);
  322. }
  323. /** Adds elements from another set to this one.
  324. @param setToAddFrom the set from which to copy the elements
  325. @param startIndex the first element of the other set to start copying from
  326. @param numElementsToAdd how many elements to add from the other set. If this
  327. value is negative or greater than the number of available elements,
  328. all available elements will be copied.
  329. @see add
  330. */
  331. template <class OtherSetType>
  332. void addSet (const OtherSetType& setToAddFrom,
  333. int startIndex = 0,
  334. int numElementsToAdd = -1) noexcept
  335. {
  336. const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
  337. {
  338. const ScopedLockType lock2 (getLock());
  339. jassert (this != &setToAddFrom);
  340. if (this != &setToAddFrom)
  341. {
  342. if (startIndex < 0)
  343. {
  344. jassertfalse;
  345. startIndex = 0;
  346. }
  347. if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
  348. numElementsToAdd = setToAddFrom.size() - startIndex;
  349. addArray (setToAddFrom.elements + startIndex, numElementsToAdd);
  350. }
  351. }
  352. }
  353. //==============================================================================
  354. /** Removes an element from the set.
  355. This will remove the element at a given index.
  356. If the index passed in is out-of-range, nothing will happen.
  357. @param indexToRemove the index of the element to remove
  358. @returns the element that has been removed
  359. @see removeValue, removeRange
  360. */
  361. ElementType remove (const int indexToRemove) noexcept
  362. {
  363. const ScopedLockType lock (getLock());
  364. if (isPositiveAndBelow (indexToRemove, numUsed))
  365. {
  366. --numUsed;
  367. ElementType* const e = data.elements + indexToRemove;
  368. ElementType const removed = *e;
  369. const int numberToShift = numUsed - indexToRemove;
  370. if (numberToShift > 0)
  371. memmove (e, e + 1, numberToShift * sizeof (ElementType));
  372. if ((numUsed << 1) < data.numAllocated)
  373. minimiseStorageOverheads();
  374. return removed;
  375. }
  376. return ElementType();
  377. }
  378. /** Removes an item from the set.
  379. This will remove the given element from the set, if it's there.
  380. @param valueToRemove the object to try to remove
  381. @see remove, removeRange
  382. */
  383. void removeValue (const ElementType valueToRemove) noexcept
  384. {
  385. const ScopedLockType lock (getLock());
  386. remove (indexOf (valueToRemove));
  387. }
  388. /** Removes any elements which are also in another set.
  389. @param otherSet the other set in which to look for elements to remove
  390. @see removeValuesNotIn, remove, removeValue, removeRange
  391. */
  392. template <class OtherSetType>
  393. void removeValuesIn (const OtherSetType& otherSet) noexcept
  394. {
  395. const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
  396. const ScopedLockType lock2 (getLock());
  397. if (this == &otherSet)
  398. {
  399. clear();
  400. }
  401. else
  402. {
  403. if (otherSet.size() > 0)
  404. {
  405. for (int i = numUsed; --i >= 0;)
  406. if (otherSet.contains (data.elements [i]))
  407. remove (i);
  408. }
  409. }
  410. }
  411. /** Removes any elements which are not found in another set.
  412. Only elements which occur in this other set will be retained.
  413. @param otherSet the set in which to look for elements NOT to remove
  414. @see removeValuesIn, remove, removeValue, removeRange
  415. */
  416. template <class OtherSetType>
  417. void removeValuesNotIn (const OtherSetType& otherSet) noexcept
  418. {
  419. const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
  420. const ScopedLockType lock2 (getLock());
  421. if (this != &otherSet)
  422. {
  423. if (otherSet.size() <= 0)
  424. {
  425. clear();
  426. }
  427. else
  428. {
  429. for (int i = numUsed; --i >= 0;)
  430. if (! otherSet.contains (data.elements [i]))
  431. remove (i);
  432. }
  433. }
  434. }
  435. //==============================================================================
  436. /** Reduces the amount of storage being used by the set.
  437. Sets typically allocate slightly more storage than they need, and after
  438. removing elements, they may have quite a lot of unused space allocated.
  439. This method will reduce the amount of allocated storage to a minimum.
  440. */
  441. void minimiseStorageOverheads() noexcept
  442. {
  443. const ScopedLockType lock (getLock());
  444. data.shrinkToNoMoreThan (numUsed);
  445. }
  446. /** Increases the set's internal storage to hold a minimum number of elements.
  447. Calling this before adding a large known number of elements means that
  448. the set won't have to keep dynamically resizing itself as the elements
  449. are added, and it'll therefore be more efficient.
  450. */
  451. void ensureStorageAllocated (const int minNumElements)
  452. {
  453. const ScopedLockType lock (getLock());
  454. data.ensureAllocatedSize (minNumElements);
  455. }
  456. //==============================================================================
  457. /** Returns the CriticalSection that locks this array.
  458. To lock, you can call getLock().enter() and getLock().exit(), or preferably use
  459. an object of ScopedLockType as an RAII lock for it.
  460. */
  461. inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data; }
  462. /** Returns the type of scoped lock to use for locking this array */
  463. typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
  464. private:
  465. //==============================================================================
  466. ArrayAllocationBase <ElementType, TypeOfCriticalSectionToUse> data;
  467. int numUsed;
  468. void insertInternal (const int indexToInsertAt, const ElementType newElement) noexcept
  469. {
  470. data.ensureAllocatedSize (numUsed + 1);
  471. ElementType* const insertPos = data.elements + indexToInsertAt;
  472. const int numberToMove = numUsed - indexToInsertAt;
  473. if (numberToMove > 0)
  474. memmove (insertPos + 1, insertPos, numberToMove * sizeof (ElementType));
  475. *insertPos = newElement;
  476. ++numUsed;
  477. }
  478. };
  479. #if JUCE_MSVC
  480. #pragma warning (pop)
  481. #endif
  482. #endif // __JUCE_SORTEDSET_JUCEHEADER__