Audio plugin host https://kx.studio/carla
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.

467 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the Water library.
  4. Copyright (c) 2016 ROLI Ltd.
  5. Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
  6. Permission is granted to use this software under the terms of the ISC license
  7. http://www.isc.org/downloads/software-support-policy/isc-license/
  8. Permission to use, copy, modify, and/or distribute this software for any
  9. purpose with or without fee is hereby granted, provided that the above
  10. copyright notice and this permission notice appear in all copies.
  11. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  12. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  13. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  14. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  15. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  16. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  17. OF THIS SOFTWARE.
  18. ==============================================================================
  19. */
  20. #ifndef WATER_SORTEDSET_H_INCLUDED
  21. #define WATER_SORTEDSET_H_INCLUDED
  22. #include "../water.h"
  23. namespace water {
  24. //==============================================================================
  25. /**
  26. Holds a set of unique primitive objects, such as ints or doubles.
  27. A set can only hold one item with a given value, so if for example it's a
  28. set of integers, attempting to add the same integer twice will do nothing
  29. the second time.
  30. Internally, the list of items is kept sorted (which means that whatever
  31. kind of primitive type is used must support the ==, <, >, <= and >= operators
  32. to determine the order), and searching the set for known values is very fast
  33. because it uses a binary-chop method.
  34. Note that if you're using a class or struct as the element type, it must be
  35. capable of being copied or moved with a straightforward memcpy, rather than
  36. needing construction and destruction code.
  37. To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
  38. TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
  39. @see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
  40. */
  41. template <class ElementType>
  42. class SortedSet
  43. {
  44. public:
  45. //==============================================================================
  46. /** Creates an empty set. */
  47. SortedSet() noexcept
  48. {
  49. }
  50. /** Creates a copy of another set.
  51. @param other the set to copy
  52. */
  53. SortedSet (const SortedSet& other)
  54. : data (other.data)
  55. {
  56. }
  57. /** Destructor. */
  58. ~SortedSet() noexcept
  59. {
  60. }
  61. /** Copies another set over this one.
  62. @param other the set to copy
  63. */
  64. SortedSet& operator= (const SortedSet& other) noexcept
  65. {
  66. data = other.data;
  67. return *this;
  68. }
  69. //==============================================================================
  70. /** Compares this set to another one.
  71. Two sets are considered equal if they both contain the same set of elements.
  72. @param other the other set to compare with
  73. */
  74. bool operator== (const SortedSet<ElementType>& other) const noexcept
  75. {
  76. return data == other.data;
  77. }
  78. /** Compares this set to another one.
  79. Two sets are considered equal if they both contain the same set of elements.
  80. @param other the other set to compare with
  81. */
  82. bool operator!= (const SortedSet<ElementType>& other) const noexcept
  83. {
  84. return ! operator== (other);
  85. }
  86. //==============================================================================
  87. /** Removes all elements from the set.
  88. This will remove all the elements, and free any storage that the set is
  89. using. To clear it without freeing the storage, use the clearQuick()
  90. method instead.
  91. @see clearQuick
  92. */
  93. void clear() noexcept
  94. {
  95. data.clear();
  96. }
  97. /** Removes all elements from the set without freeing the array's allocated storage.
  98. @see clear
  99. */
  100. void clearQuick() noexcept
  101. {
  102. data.clearQuick();
  103. }
  104. //==============================================================================
  105. /** Returns the current number of elements in the set. */
  106. inline int size() const noexcept
  107. {
  108. return data.size();
  109. }
  110. /** Returns true if the set is empty, false otherwise. */
  111. inline bool isEmpty() const noexcept
  112. {
  113. return size() == 0;
  114. }
  115. /** Returns one of the elements in the set.
  116. If the index passed in is beyond the range of valid elements, this
  117. will return zero.
  118. If you're certain that the index will always be a valid element, you
  119. can call getUnchecked() instead, which is faster.
  120. @param index the index of the element being requested (0 is the first element in the set)
  121. @see getUnchecked, getFirst, getLast
  122. */
  123. inline ElementType operator[] (const int index) const noexcept
  124. {
  125. return data [index];
  126. }
  127. /** Returns one of the elements in the set, without checking the index passed in.
  128. Unlike the operator[] method, this will try to return an element without
  129. checking that the index is within the bounds of the set, so should only
  130. be used when you're confident that it will always be a valid index.
  131. @param index the index of the element being requested (0 is the first element in the set)
  132. @see operator[], getFirst, getLast
  133. */
  134. inline ElementType getUnchecked (const int index) const noexcept
  135. {
  136. return data.getUnchecked (index);
  137. }
  138. /** Returns a direct reference to one of the elements in the set, without checking the index passed in.
  139. This is like getUnchecked, but returns a direct reference to the element, so that
  140. you can alter it directly. Obviously this can be dangerous, so only use it when
  141. absolutely necessary.
  142. @param index the index of the element being requested (0 is the first element in the array)
  143. */
  144. inline ElementType& getReference (const int index) const noexcept
  145. {
  146. return data.getReference (index);
  147. }
  148. /** Returns the first element in the set, or 0 if the set is empty.
  149. @see operator[], getUnchecked, getLast
  150. */
  151. inline ElementType getFirst() const noexcept
  152. {
  153. return data.getFirst();
  154. }
  155. /** Returns the last element in the set, or 0 if the set is empty.
  156. @see operator[], getUnchecked, getFirst
  157. */
  158. inline ElementType getLast() const noexcept
  159. {
  160. return data.getLast();
  161. }
  162. //==============================================================================
  163. /** Returns a pointer to the first element in the set.
  164. This method is provided for compatibility with standard C++ iteration mechanisms.
  165. */
  166. inline ElementType* begin() const noexcept
  167. {
  168. return data.begin();
  169. }
  170. /** Returns a pointer to the element which follows the last element in the set.
  171. This method is provided for compatibility with standard C++ iteration mechanisms.
  172. */
  173. inline ElementType* end() const noexcept
  174. {
  175. return data.end();
  176. }
  177. //==============================================================================
  178. /** Finds the index of the first element which matches the value passed in.
  179. This will search the set for the given object, and return the index
  180. of its first occurrence. If the object isn't found, the method will return -1.
  181. @param elementToLookFor the value or object to look for
  182. @returns the index of the object, or -1 if it's not found
  183. */
  184. int indexOf (const ElementType& elementToLookFor) const noexcept
  185. {
  186. int s = 0;
  187. int e = data.size();
  188. for (;;)
  189. {
  190. if (s >= e)
  191. return -1;
  192. if (elementToLookFor == data.getReference (s))
  193. return s;
  194. const int halfway = (s + e) / 2;
  195. if (halfway == s)
  196. return -1;
  197. if (elementToLookFor < data.getReference (halfway))
  198. e = halfway;
  199. else
  200. s = halfway;
  201. }
  202. }
  203. /** Returns true if the set contains at least one occurrence of an object.
  204. @param elementToLookFor the value or object to look for
  205. @returns true if the item is found
  206. */
  207. bool contains (const ElementType& elementToLookFor) const noexcept
  208. {
  209. return indexOf (elementToLookFor) >= 0;
  210. }
  211. //==============================================================================
  212. /** Adds a new element to the set, (as long as it's not already in there).
  213. Note that if a matching element already exists, the new value will be assigned
  214. to the existing one using operator=, so that if there are any differences between
  215. the objects which were not recognised by the object's operator==, then the
  216. set will always contain a copy of the most recently added one.
  217. @param newElement the new object to add to the set
  218. @returns true if the value was added, or false if it already existed
  219. @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
  220. */
  221. bool add (const ElementType& newElement) noexcept
  222. {
  223. int s = 0;
  224. int e = data.size();
  225. while (s < e)
  226. {
  227. ElementType& elem = data.getReference (s);
  228. if (newElement == elem)
  229. {
  230. elem = newElement; // force an update in case operator== permits differences.
  231. return false;
  232. }
  233. const int halfway = (s + e) / 2;
  234. const bool isBeforeHalfway = (newElement < data.getReference (halfway));
  235. if (halfway == s)
  236. {
  237. if (! isBeforeHalfway)
  238. ++s;
  239. break;
  240. }
  241. if (isBeforeHalfway)
  242. e = halfway;
  243. else
  244. s = halfway;
  245. }
  246. data.insert (s, newElement);
  247. return true;
  248. }
  249. /** Adds elements from an array to this set.
  250. @param elementsToAdd the array of elements to add
  251. @param numElementsToAdd how many elements are in this other array
  252. @see add
  253. */
  254. void addArray (const ElementType* elementsToAdd,
  255. int numElementsToAdd) noexcept
  256. {
  257. while (--numElementsToAdd >= 0)
  258. add (*elementsToAdd++);
  259. }
  260. /** Adds elements from another set to this one.
  261. @param setToAddFrom the set from which to copy the elements
  262. @param startIndex the first element of the other set to start copying from
  263. @param numElementsToAdd how many elements to add from the other set. If this
  264. value is negative or greater than the number of available elements,
  265. all available elements will be copied.
  266. @see add
  267. */
  268. template <class OtherSetType>
  269. void addSet (const OtherSetType& setToAddFrom,
  270. int startIndex = 0,
  271. int numElementsToAdd = -1) noexcept
  272. {
  273. wassert (this != &setToAddFrom);
  274. if (this != &setToAddFrom)
  275. {
  276. if (startIndex < 0)
  277. {
  278. wassertfalse;
  279. startIndex = 0;
  280. }
  281. if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
  282. numElementsToAdd = setToAddFrom.size() - startIndex;
  283. if (numElementsToAdd > 0)
  284. addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
  285. }
  286. }
  287. //==============================================================================
  288. /** Removes an element from the set.
  289. This will remove the element at a given index.
  290. If the index passed in is out-of-range, nothing will happen.
  291. @param indexToRemove the index of the element to remove
  292. @returns the element that has been removed
  293. @see removeValue, removeRange
  294. */
  295. ElementType remove (const int indexToRemove) noexcept
  296. {
  297. return data.removeAndReturn (indexToRemove);
  298. }
  299. /** Removes an item from the set.
  300. This will remove the given element from the set, if it's there.
  301. @param valueToRemove the object to try to remove
  302. @see remove, removeRange
  303. */
  304. void removeValue (const ElementType valueToRemove) noexcept
  305. {
  306. data.remove (indexOf (valueToRemove));
  307. }
  308. /** Removes any elements which are also in another set.
  309. @param otherSet the other set in which to look for elements to remove
  310. @see removeValuesNotIn, remove, removeValue, removeRange
  311. */
  312. template <class OtherSetType>
  313. void removeValuesIn (const OtherSetType& otherSet) noexcept
  314. {
  315. if (this == &otherSet)
  316. {
  317. clear();
  318. }
  319. else if (otherSet.size() > 0)
  320. {
  321. for (int i = data.size(); --i >= 0;)
  322. if (otherSet.contains (data.getReference (i)))
  323. remove (i);
  324. }
  325. }
  326. /** Removes any elements which are not found in another set.
  327. Only elements which occur in this other set will be retained.
  328. @param otherSet the set in which to look for elements NOT to remove
  329. @see removeValuesIn, remove, removeValue, removeRange
  330. */
  331. template <class OtherSetType>
  332. void removeValuesNotIn (const OtherSetType& otherSet) noexcept
  333. {
  334. if (this != &otherSet)
  335. {
  336. if (otherSet.size() <= 0)
  337. {
  338. clear();
  339. }
  340. else
  341. {
  342. for (int i = data.size(); --i >= 0;)
  343. if (! otherSet.contains (data.getReference (i)))
  344. remove (i);
  345. }
  346. }
  347. }
  348. /** This swaps the contents of this array with those of another array.
  349. If you need to exchange two arrays, this is vastly quicker than using copy-by-value
  350. because it just swaps their internal pointers.
  351. */
  352. template <class OtherSetType>
  353. void swapWith (OtherSetType& otherSet) noexcept
  354. {
  355. data.swapWith (otherSet.data);
  356. }
  357. //==============================================================================
  358. /** Reduces the amount of storage being used by the set.
  359. Sets typically allocate slightly more storage than they need, and after
  360. removing elements, they may have quite a lot of unused space allocated.
  361. This method will reduce the amount of allocated storage to a minimum.
  362. */
  363. void minimiseStorageOverheads() noexcept
  364. {
  365. data.minimiseStorageOverheads();
  366. }
  367. /** Increases the set's internal storage to hold a minimum number of elements.
  368. Calling this before adding a large known number of elements means that
  369. the set won't have to keep dynamically resizing itself as the elements
  370. are added, and it'll therefore be more efficient.
  371. */
  372. void ensureStorageAllocated (const int minNumElements)
  373. {
  374. data.ensureStorageAllocated (minNumElements);
  375. }
  376. private:
  377. //==============================================================================
  378. Array<ElementType> data;
  379. };
  380. }
  381. #endif // WATER_SORTEDSET_H_INCLUDED