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.

456 lines
16KB

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