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.

450 lines
15KB

  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_HASHMAP_JUCEHEADER__
  19. #define __JUCE_HASHMAP_JUCEHEADER__
  20. #include "juce_OwnedArray.h"
  21. #include "juce_LinkedListPointer.h"
  22. #include "../memory/juce_ScopedPointer.h"
  23. //==============================================================================
  24. /**
  25. A simple class to generate hash functions for some primitive types, intended for
  26. use with the HashMap class.
  27. @see HashMap
  28. */
  29. class DefaultHashFunctions
  30. {
  31. public:
  32. /** Generates a simple hash from an integer. */
  33. static int generateHash (const int key, const int upperLimit) noexcept { return std::abs (key) % upperLimit; }
  34. /** Generates a simple hash from an int64. */
  35. static int generateHash (const int64 key, const int upperLimit) noexcept { return std::abs ((int) key) % upperLimit; }
  36. /** Generates a simple hash from a string. */
  37. static int generateHash (const String& key, const int upperLimit) noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); }
  38. /** Generates a simple hash from a variant. */
  39. static int generateHash (const var& key, const int upperLimit) noexcept { return generateHash (key.toString(), upperLimit); }
  40. };
  41. //==============================================================================
  42. /**
  43. Holds a set of mappings between some key/value pairs.
  44. The types of the key and value objects are set as template parameters.
  45. You can also specify a class to supply a hash function that converts a key value
  46. into an hashed integer. This class must have the form:
  47. @code
  48. struct MyHashGenerator
  49. {
  50. static int generateHash (MyKeyType key, int upperLimit)
  51. {
  52. // The function must return a value 0 <= x < upperLimit
  53. return someFunctionOfMyKeyType (key) % upperLimit;
  54. }
  55. };
  56. @endcode
  57. Like the Array class, the key and value types are expected to be copy-by-value types, so
  58. if you define them to be pointer types, this class won't delete the objects that they
  59. point to.
  60. If you don't supply a class for the HashFunctionToUse template parameter, the
  61. default one provides some simple mappings for strings and ints.
  62. @code
  63. HashMap<int, String> hash;
  64. hash.set (1, "item1");
  65. hash.set (2, "item2");
  66. DBG (hash [1]); // prints "item1"
  67. DBG (hash [2]); // prints "item2"
  68. // This iterates the map, printing all of its key -> value pairs..
  69. for (HashMap<int, String>::Iterator i (hash); i.next();)
  70. DBG (i.getKey() << " -> " << i.getValue());
  71. @endcode
  72. @see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet
  73. */
  74. template <typename KeyType,
  75. typename ValueType,
  76. class HashFunctionToUse = DefaultHashFunctions,
  77. class TypeOfCriticalSectionToUse = DummyCriticalSection>
  78. class HashMap
  79. {
  80. private:
  81. typedef PARAMETER_TYPE (KeyType) KeyTypeParameter;
  82. typedef PARAMETER_TYPE (ValueType) ValueTypeParameter;
  83. public:
  84. //==============================================================================
  85. /** Creates an empty hash-map.
  86. The numberOfSlots parameter specifies the number of hash entries the map will use. This
  87. will be the "upperLimit" parameter that is passed to your generateHash() function. The number
  88. of hash slots will grow automatically if necessary, or it can be remapped manually using remapTable().
  89. */
  90. explicit HashMap (const int numberOfSlots = defaultHashTableSize)
  91. : totalNumItems (0)
  92. {
  93. slots.insertMultiple (0, nullptr, numberOfSlots);
  94. }
  95. /** Destructor. */
  96. ~HashMap()
  97. {
  98. clear();
  99. }
  100. //==============================================================================
  101. /** Removes all values from the map.
  102. Note that this will clear the content, but won't affect the number of slots (see
  103. remapTable and getNumSlots).
  104. */
  105. void clear()
  106. {
  107. const ScopedLockType sl (getLock());
  108. for (int i = slots.size(); --i >= 0;)
  109. {
  110. HashEntry* h = slots.getUnchecked(i);
  111. while (h != nullptr)
  112. {
  113. const ScopedPointer<HashEntry> deleter (h);
  114. h = h->nextEntry;
  115. }
  116. slots.set (i, nullptr);
  117. }
  118. totalNumItems = 0;
  119. }
  120. //==============================================================================
  121. /** Returns the current number of items in the map. */
  122. inline int size() const noexcept
  123. {
  124. return totalNumItems;
  125. }
  126. /** Returns the value corresponding to a given key.
  127. If the map doesn't contain the key, a default instance of the value type is returned.
  128. @param keyToLookFor the key of the item being requested
  129. */
  130. inline ValueType operator[] (KeyTypeParameter keyToLookFor) const
  131. {
  132. const ScopedLockType sl (getLock());
  133. for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry)
  134. if (entry->key == keyToLookFor)
  135. return entry->value;
  136. return ValueType();
  137. }
  138. //==============================================================================
  139. /** Returns true if the map contains an item with the specied key. */
  140. bool contains (KeyTypeParameter keyToLookFor) const
  141. {
  142. const ScopedLockType sl (getLock());
  143. for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry)
  144. if (entry->key == keyToLookFor)
  145. return true;
  146. return false;
  147. }
  148. /** Returns true if the hash contains at least one occurrence of a given value. */
  149. bool containsValue (ValueTypeParameter valueToLookFor) const
  150. {
  151. const ScopedLockType sl (getLock());
  152. for (int i = getNumSlots(); --i >= 0;)
  153. for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
  154. if (entry->value == valueToLookFor)
  155. return true;
  156. return false;
  157. }
  158. //==============================================================================
  159. /** Adds or replaces an element in the hash-map.
  160. If there's already an item with the given key, this will replace its value. Otherwise, a new item
  161. will be added to the map.
  162. */
  163. void set (KeyTypeParameter newKey, ValueTypeParameter newValue)
  164. {
  165. const ScopedLockType sl (getLock());
  166. const int hashIndex = generateHashFor (newKey);
  167. HashEntry* const firstEntry = slots.getUnchecked (hashIndex);
  168. for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry)
  169. {
  170. if (entry->key == newKey)
  171. {
  172. entry->value = newValue;
  173. return;
  174. }
  175. }
  176. slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry));
  177. ++totalNumItems;
  178. if (totalNumItems > (getNumSlots() * 3) / 2)
  179. remapTable (getNumSlots() * 2);
  180. }
  181. /** Removes an item with the given key. */
  182. void remove (KeyTypeParameter keyToRemove)
  183. {
  184. const ScopedLockType sl (getLock());
  185. const int hashIndex = generateHashFor (keyToRemove);
  186. HashEntry* entry = slots.getUnchecked (hashIndex);
  187. HashEntry* previous = nullptr;
  188. while (entry != nullptr)
  189. {
  190. if (entry->key == keyToRemove)
  191. {
  192. const ScopedPointer<HashEntry> deleter (entry);
  193. entry = entry->nextEntry;
  194. if (previous != nullptr)
  195. previous->nextEntry = entry;
  196. else
  197. slots.set (hashIndex, entry);
  198. --totalNumItems;
  199. }
  200. else
  201. {
  202. previous = entry;
  203. entry = entry->nextEntry;
  204. }
  205. }
  206. }
  207. /** Removes all items with the given value. */
  208. void removeValue (ValueTypeParameter valueToRemove)
  209. {
  210. const ScopedLockType sl (getLock());
  211. for (int i = getNumSlots(); --i >= 0;)
  212. {
  213. HashEntry* entry = slots.getUnchecked(i);
  214. HashEntry* previous = nullptr;
  215. while (entry != nullptr)
  216. {
  217. if (entry->value == valueToRemove)
  218. {
  219. const ScopedPointer<HashEntry> deleter (entry);
  220. entry = entry->nextEntry;
  221. if (previous != nullptr)
  222. previous->nextEntry = entry;
  223. else
  224. slots.set (i, entry);
  225. --totalNumItems;
  226. }
  227. else
  228. {
  229. previous = entry;
  230. entry = entry->nextEntry;
  231. }
  232. }
  233. }
  234. }
  235. /** Remaps the hash-map to use a different number of slots for its hash function.
  236. Each slot corresponds to a single hash-code, and each one can contain multiple items.
  237. @see getNumSlots()
  238. */
  239. void remapTable (int newNumberOfSlots)
  240. {
  241. HashMap newTable (newNumberOfSlots);
  242. for (int i = getNumSlots(); --i >= 0;)
  243. for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry)
  244. newTable.set (entry->key, entry->value);
  245. swapWith (newTable);
  246. }
  247. /** Returns the number of slots which are available for hashing.
  248. Each slot corresponds to a single hash-code, and each one can contain multiple items.
  249. @see getNumSlots()
  250. */
  251. inline int getNumSlots() const noexcept
  252. {
  253. return slots.size();
  254. }
  255. //==============================================================================
  256. /** Efficiently swaps the contents of two hash-maps. */
  257. void swapWith (HashMap& otherHashMap) noexcept
  258. {
  259. const ScopedLockType lock1 (getLock());
  260. const ScopedLockType lock2 (otherHashMap.getLock());
  261. slots.swapWithArray (otherHashMap.slots);
  262. std::swap (totalNumItems, otherHashMap.totalNumItems);
  263. }
  264. //==============================================================================
  265. /** Returns the CriticalSection that locks this structure.
  266. To lock, you can call getLock().enter() and getLock().exit(), or preferably use
  267. an object of ScopedLockType as an RAII lock for it.
  268. */
  269. inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return lock; }
  270. /** Returns the type of scoped lock to use for locking this array */
  271. typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;
  272. private:
  273. //==============================================================================
  274. class HashEntry
  275. {
  276. public:
  277. HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next)
  278. : key (k), value (val), nextEntry (next)
  279. {}
  280. const KeyType key;
  281. ValueType value;
  282. HashEntry* nextEntry;
  283. JUCE_DECLARE_NON_COPYABLE (HashEntry)
  284. };
  285. public:
  286. //==============================================================================
  287. /** Iterates over the items in a HashMap.
  288. To use it, repeatedly call next() until it returns false, e.g.
  289. @code
  290. HashMap <String, String> myMap;
  291. HashMap<String, String>::Iterator i (myMap);
  292. while (i.next())
  293. {
  294. DBG (i.getKey() << " -> " << i.getValue());
  295. }
  296. @endcode
  297. The order in which items are iterated bears no resemblence to the order in which
  298. they were originally added!
  299. Obviously as soon as you call any non-const methods on the original hash-map, any
  300. iterators that were created beforehand will cease to be valid, and should not be used.
  301. @see HashMap
  302. */
  303. class Iterator
  304. {
  305. public:
  306. //==============================================================================
  307. Iterator (const HashMap& hashMapToIterate)
  308. : hashMap (hashMapToIterate), entry (nullptr), index (0)
  309. {}
  310. /** Moves to the next item, if one is available.
  311. When this returns true, you can get the item's key and value using getKey() and
  312. getValue(). If it returns false, the iteration has finished and you should stop.
  313. */
  314. bool next()
  315. {
  316. if (entry != nullptr)
  317. entry = entry->nextEntry;
  318. while (entry == nullptr)
  319. {
  320. if (index >= hashMap.getNumSlots())
  321. return false;
  322. entry = hashMap.slots.getUnchecked (index++);
  323. }
  324. return true;
  325. }
  326. /** Returns the current item's key.
  327. This should only be called when a call to next() has just returned true.
  328. */
  329. KeyType getKey() const
  330. {
  331. return entry != nullptr ? entry->key : KeyType();
  332. }
  333. /** Returns the current item's value.
  334. This should only be called when a call to next() has just returned true.
  335. */
  336. ValueType getValue() const
  337. {
  338. return entry != nullptr ? entry->value : ValueType();
  339. }
  340. private:
  341. //==============================================================================
  342. const HashMap& hashMap;
  343. HashEntry* entry;
  344. int index;
  345. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator)
  346. };
  347. private:
  348. //==============================================================================
  349. enum { defaultHashTableSize = 101 };
  350. friend class Iterator;
  351. Array <HashEntry*> slots;
  352. int totalNumItems;
  353. TypeOfCriticalSectionToUse lock;
  354. int generateHashFor (KeyTypeParameter key) const
  355. {
  356. const int hash = HashFunctionToUse::generateHash (key, getNumSlots());
  357. jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers!
  358. return hash;
  359. }
  360. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap)
  361. };
  362. #endif // __JUCE_HASHMAP_JUCEHEADER__