EnumerableMap.sol 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity >=0.6.0 <0.8.0;
  3. /**
  4. * @dev Library for managing an enumerable variant of Solidity's
  5. * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
  6. * type.
  7. *
  8. * Maps have the following properties:
  9. *
  10. * - Entries are added, removed, and checked for existence in constant time
  11. * (O(1)).
  12. * - Entries are enumerated in O(n). No guarantees are made on the ordering.
  13. *
  14. * ```
  15. * contract Example {
  16. * // Add the library methods
  17. * using EnumerableMap for EnumerableMap.UintToAddressMap;
  18. *
  19. * // Declare a set state variable
  20. * EnumerableMap.UintToAddressMap private myMap;
  21. * }
  22. * ```
  23. *
  24. * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
  25. * supported.
  26. */
  27. library EnumerableMap {
  28. // To implement this library for multiple types with as little code
  29. // repetition as possible, we write it in terms of a generic Map type with
  30. // bytes32 keys and values.
  31. // The Map implementation uses private functions, and user-facing
  32. // implementations (such as Uint256ToAddressMap) are just wrappers around
  33. // the underlying Map.
  34. // This means that we can only create new EnumerableMaps for types that fit
  35. // in bytes32.
  36. struct MapEntry {
  37. bytes32 _key;
  38. bytes32 _value;
  39. }
  40. struct Map {
  41. // Storage of map keys and values
  42. MapEntry[] _entries;
  43. // Position of the entry defined by a key in the `entries` array, plus 1
  44. // because index 0 means a key is not in the map.
  45. mapping (bytes32 => uint256) _indexes;
  46. }
  47. /**
  48. * @dev Adds a key-value pair to a map, or updates the value for an existing
  49. * key. O(1).
  50. *
  51. * Returns true if the key was added to the map, that is if it was not
  52. * already present.
  53. */
  54. function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
  55. // We read and store the key's index to prevent multiple reads from the same storage slot
  56. uint256 keyIndex = map._indexes[key];
  57. if (keyIndex == 0) { // Equivalent to !contains(map, key)
  58. map._entries.push(MapEntry({ _key: key, _value: value }));
  59. // The entry is stored at length-1, but we add 1 to all indexes
  60. // and use 0 as a sentinel value
  61. map._indexes[key] = map._entries.length;
  62. return true;
  63. } else {
  64. map._entries[keyIndex - 1]._value = value;
  65. return false;
  66. }
  67. }
  68. /**
  69. * @dev Removes a key-value pair from a map. O(1).
  70. *
  71. * Returns true if the key was removed from the map, that is if it was present.
  72. */
  73. function _remove(Map storage map, bytes32 key) private returns (bool) {
  74. // We read and store the key's index to prevent multiple reads from the same storage slot
  75. uint256 keyIndex = map._indexes[key];
  76. if (keyIndex != 0) { // Equivalent to contains(map, key)
  77. // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
  78. // in the array, and then remove the last entry (sometimes called as 'swap and pop').
  79. // This modifies the order of the array, as noted in {at}.
  80. uint256 toDeleteIndex = keyIndex - 1;
  81. uint256 lastIndex = map._entries.length - 1;
  82. // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
  83. // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
  84. MapEntry storage lastEntry = map._entries[lastIndex];
  85. // Move the last entry to the index where the entry to delete is
  86. map._entries[toDeleteIndex] = lastEntry;
  87. // Update the index for the moved entry
  88. map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
  89. // Delete the slot where the moved entry was stored
  90. map._entries.pop();
  91. // Delete the index for the deleted slot
  92. delete map._indexes[key];
  93. return true;
  94. } else {
  95. return false;
  96. }
  97. }
  98. /**
  99. * @dev Returns true if the key is in the map. O(1).
  100. */
  101. function _contains(Map storage map, bytes32 key) private view returns (bool) {
  102. return map._indexes[key] != 0;
  103. }
  104. /**
  105. * @dev Returns the number of key-value pairs in the map. O(1).
  106. */
  107. function _length(Map storage map) private view returns (uint256) {
  108. return map._entries.length;
  109. }
  110. /**
  111. * @dev Returns the key-value pair stored at position `index` in the map. O(1).
  112. *
  113. * Note that there are no guarantees on the ordering of entries inside the
  114. * array, and it may change when more entries are added or removed.
  115. *
  116. * Requirements:
  117. *
  118. * - `index` must be strictly less than {length}.
  119. */
  120. function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
  121. require(map._entries.length > index, "EnumerableMap: index out of bounds");
  122. MapEntry storage entry = map._entries[index];
  123. return (entry._key, entry._value);
  124. }
  125. /**
  126. * @dev Tries to returns the value associated with `key`. O(1).
  127. * Does not revert if `key` is not in the map.
  128. */
  129. function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
  130. uint256 keyIndex = map._indexes[key];
  131. if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
  132. return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
  133. }
  134. /**
  135. * @dev Returns the value associated with `key`. O(1).
  136. *
  137. * Requirements:
  138. *
  139. * - `key` must be in the map.
  140. */
  141. function _get(Map storage map, bytes32 key) private view returns (bytes32) {
  142. uint256 keyIndex = map._indexes[key];
  143. require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
  144. return map._entries[keyIndex - 1]._value; // All indexes are 1-based
  145. }
  146. /**
  147. * @dev Same as {_get}, with a custom error message when `key` is not in the map.
  148. *
  149. * CAUTION: This function is deprecated because it requires allocating memory for the error
  150. * message unnecessarily. For custom revert reasons use {_tryGet}.
  151. */
  152. function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
  153. uint256 keyIndex = map._indexes[key];
  154. require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
  155. return map._entries[keyIndex - 1]._value; // All indexes are 1-based
  156. }
  157. // UintToAddressMap
  158. struct UintToAddressMap {
  159. Map _inner;
  160. }
  161. /**
  162. * @dev Adds a key-value pair to a map, or updates the value for an existing
  163. * key. O(1).
  164. *
  165. * Returns true if the key was added to the map, that is if it was not
  166. * already present.
  167. */
  168. function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
  169. return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
  170. }
  171. /**
  172. * @dev Removes a value from a set. O(1).
  173. *
  174. * Returns true if the key was removed from the map, that is if it was present.
  175. */
  176. function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
  177. return _remove(map._inner, bytes32(key));
  178. }
  179. /**
  180. * @dev Returns true if the key is in the map. O(1).
  181. */
  182. function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
  183. return _contains(map._inner, bytes32(key));
  184. }
  185. /**
  186. * @dev Returns the number of elements in the map. O(1).
  187. */
  188. function length(UintToAddressMap storage map) internal view returns (uint256) {
  189. return _length(map._inner);
  190. }
  191. /**
  192. * @dev Returns the element stored at position `index` in the set. O(1).
  193. * Note that there are no guarantees on the ordering of values inside the
  194. * array, and it may change when more values are added or removed.
  195. *
  196. * Requirements:
  197. *
  198. * - `index` must be strictly less than {length}.
  199. */
  200. function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
  201. (bytes32 key, bytes32 value) = _at(map._inner, index);
  202. return (uint256(key), address(uint160(uint256(value))));
  203. }
  204. /**
  205. * @dev Tries to returns the value associated with `key`. O(1).
  206. * Does not revert if `key` is not in the map.
  207. */
  208. function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
  209. (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
  210. return (success, address(uint160(uint256(value))));
  211. }
  212. /**
  213. * @dev Returns the value associated with `key`. O(1).
  214. *
  215. * Requirements:
  216. *
  217. * - `key` must be in the map.
  218. */
  219. function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
  220. return address(uint160(uint256(_get(map._inner, bytes32(key)))));
  221. }
  222. /**
  223. * @dev Same as {get}, with a custom error message when `key` is not in the map.
  224. *
  225. * CAUTION: This function is deprecated because it requires allocating memory for the error
  226. * message unnecessarily. For custom revert reasons use {tryGet}.
  227. */
  228. function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
  229. return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
  230. }
  231. }