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.

507 lines
18KB

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