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.

300 lines
9.3KB

  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_SPARSESET_JUCEHEADER__
  19. #define __JUCE_SPARSESET_JUCEHEADER__
  20. #include "../maths/juce_Range.h"
  21. #include "../threads/juce_CriticalSection.h"
  22. //==============================================================================
  23. /**
  24. Holds a set of primitive values, storing them as a set of ranges.
  25. This container acts like an array, but can efficiently hold large continguous
  26. ranges of values. It's quite a specialised class, mostly useful for things
  27. like keeping the set of selected rows in a listbox.
  28. The type used as a template paramter must be an integer type, such as int, short,
  29. int64, etc.
  30. */
  31. template <class Type>
  32. class SparseSet
  33. {
  34. public:
  35. //==============================================================================
  36. /** Creates a new empty set. */
  37. SparseSet()
  38. {
  39. }
  40. /** Creates a copy of another SparseSet. */
  41. SparseSet (const SparseSet<Type>& other)
  42. : values (other.values)
  43. {
  44. }
  45. //==============================================================================
  46. /** Clears the set. */
  47. void clear()
  48. {
  49. values.clear();
  50. }
  51. /** Checks whether the set is empty.
  52. This is much quicker than using (size() == 0).
  53. */
  54. bool isEmpty() const noexcept
  55. {
  56. return values.size() == 0;
  57. }
  58. /** Returns the number of values in the set.
  59. Because of the way the data is stored, this method can take longer if there
  60. are a lot of items in the set. Use isEmpty() for a quick test of whether there
  61. are any items.
  62. */
  63. Type size() const
  64. {
  65. Type total (0);
  66. for (int i = 0; i < values.size(); i += 2)
  67. total += values.getUnchecked (i + 1) - values.getUnchecked (i);
  68. return total;
  69. }
  70. /** Returns one of the values in the set.
  71. @param index the index of the value to retrieve, in the range 0 to (size() - 1).
  72. @returns the value at this index, or 0 if it's out-of-range
  73. */
  74. Type operator[] (Type index) const
  75. {
  76. for (int i = 0; i < values.size(); i += 2)
  77. {
  78. const Type start (values.getUnchecked (i));
  79. const Type len (values.getUnchecked (i + 1) - start);
  80. if (index < len)
  81. return start + index;
  82. index -= len;
  83. }
  84. return Type();
  85. }
  86. /** Checks whether a particular value is in the set. */
  87. bool contains (const Type valueToLookFor) const
  88. {
  89. for (int i = 0; i < values.size(); ++i)
  90. if (valueToLookFor < values.getUnchecked(i))
  91. return (i & 1) != 0;
  92. return false;
  93. }
  94. //==============================================================================
  95. /** Returns the number of contiguous blocks of values.
  96. @see getRange
  97. */
  98. int getNumRanges() const noexcept
  99. {
  100. return values.size() >> 1;
  101. }
  102. /** Returns one of the contiguous ranges of values stored.
  103. @param rangeIndex the index of the range to look up, between 0
  104. and (getNumRanges() - 1)
  105. @see getTotalRange
  106. */
  107. const Range<Type> getRange (const int rangeIndex) const
  108. {
  109. if (isPositiveAndBelow (rangeIndex, getNumRanges()))
  110. return Range<Type> (values.getUnchecked (rangeIndex << 1),
  111. values.getUnchecked ((rangeIndex << 1) + 1));
  112. else
  113. return Range<Type>();
  114. }
  115. /** Returns the range between the lowest and highest values in the set.
  116. @see getRange
  117. */
  118. Range<Type> getTotalRange() const
  119. {
  120. if (values.size() > 0)
  121. {
  122. jassert ((values.size() & 1) == 0);
  123. return Range<Type> (values.getUnchecked (0),
  124. values.getUnchecked (values.size() - 1));
  125. }
  126. return Range<Type>();
  127. }
  128. //==============================================================================
  129. /** Adds a range of contiguous values to the set.
  130. e.g. addRange (Range \<int\> (10, 14)) will add (10, 11, 12, 13) to the set.
  131. */
  132. void addRange (const Range<Type>& range)
  133. {
  134. jassert (range.getLength() >= 0);
  135. if (range.getLength() > 0)
  136. {
  137. removeRange (range);
  138. values.addUsingDefaultSort (range.getStart());
  139. values.addUsingDefaultSort (range.getEnd());
  140. simplify();
  141. }
  142. }
  143. /** Removes a range of values from the set.
  144. e.g. removeRange (Range\<int\> (10, 14)) will remove (10, 11, 12, 13) from the set.
  145. */
  146. void removeRange (const Range<Type>& rangeToRemove)
  147. {
  148. jassert (rangeToRemove.getLength() >= 0);
  149. if (rangeToRemove.getLength() > 0
  150. && values.size() > 0
  151. && rangeToRemove.getStart() < values.getUnchecked (values.size() - 1)
  152. && values.getUnchecked(0) < rangeToRemove.getEnd())
  153. {
  154. const bool onAtStart = contains (rangeToRemove.getStart() - 1);
  155. const Type lastValue (jmin (rangeToRemove.getEnd(), values.getLast()));
  156. const bool onAtEnd = contains (lastValue);
  157. for (int i = values.size(); --i >= 0;)
  158. {
  159. if (values.getUnchecked(i) <= lastValue)
  160. {
  161. while (values.getUnchecked(i) >= rangeToRemove.getStart())
  162. {
  163. values.remove (i);
  164. if (--i < 0)
  165. break;
  166. }
  167. break;
  168. }
  169. }
  170. if (onAtStart) values.addUsingDefaultSort (rangeToRemove.getStart());
  171. if (onAtEnd) values.addUsingDefaultSort (lastValue);
  172. simplify();
  173. }
  174. }
  175. /** Does an XOR of the values in a given range. */
  176. void invertRange (const Range<Type>& range)
  177. {
  178. SparseSet newItems;
  179. newItems.addRange (range);
  180. int i;
  181. for (i = getNumRanges(); --i >= 0;)
  182. newItems.removeRange (getRange (i));
  183. removeRange (range);
  184. for (i = newItems.getNumRanges(); --i >= 0;)
  185. addRange (newItems.getRange(i));
  186. }
  187. /** Checks whether any part of a given range overlaps any part of this set. */
  188. bool overlapsRange (const Range<Type>& range)
  189. {
  190. if (range.getLength() > 0)
  191. {
  192. for (int i = getNumRanges(); --i >= 0;)
  193. {
  194. if (values.getUnchecked ((i << 1) + 1) <= range.getStart())
  195. return false;
  196. if (values.getUnchecked (i << 1) < range.getEnd())
  197. return true;
  198. }
  199. }
  200. return false;
  201. }
  202. /** Checks whether the whole of a given range is contained within this one. */
  203. bool containsRange (const Range<Type>& range)
  204. {
  205. if (range.getLength() > 0)
  206. {
  207. for (int i = getNumRanges(); --i >= 0;)
  208. {
  209. if (values.getUnchecked ((i << 1) + 1) <= range.getStart())
  210. return false;
  211. if (values.getUnchecked (i << 1) <= range.getStart()
  212. && range.getEnd() <= values.getUnchecked ((i << 1) + 1))
  213. return true;
  214. }
  215. }
  216. return false;
  217. }
  218. //==============================================================================
  219. bool operator== (const SparseSet<Type>& other) noexcept
  220. {
  221. return values == other.values;
  222. }
  223. bool operator!= (const SparseSet<Type>& other) noexcept
  224. {
  225. return values != other.values;
  226. }
  227. private:
  228. //==============================================================================
  229. // alternating start/end values of ranges of values that are present.
  230. Array<Type, DummyCriticalSection> values;
  231. void simplify()
  232. {
  233. jassert ((values.size() & 1) == 0);
  234. for (int i = values.size(); --i > 0;)
  235. if (values.getUnchecked(i) == values.getUnchecked (i - 1))
  236. values.removeRange (--i, 2);
  237. }
  238. };
  239. #endif // __JUCE_SPARSESET_JUCEHEADER__