123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.6.0;
- /**
- * @dev Library for managing an enumerable variant of Solidity's
- * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
- * type.
- *
- * Maps have the following properties:
- *
- * - Entries are added, removed, and checked for existence in constant time
- * (O(1)).
- * - Entries are enumerated in O(n). No guarantees are made on the ordering.
- *
- * ```
- * contract Example {
- * // Add the library methods
- * using EnumerableMap for EnumerableMap.UintToAddressMap;
- *
- * // Declare a set state variable
- * EnumerableMap.UintToAddressMap private myMap;
- * }
- * ```
- *
- * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
- * supported.
- */
- library EnumerableMap {
- // To implement this library for multiple types with as little code
- // repetition as possible, we write it in terms of a generic Map type with
- // bytes32 keys and values.
- // The Map implementation uses private functions, and user-facing
- // implementations (such as Uint256ToAddressMap) are just wrappers around
- // the underlying Map.
- // This means that we can only create new EnumerableMaps for types that fit
- // in bytes32.
- struct MapEntry {
- bytes32 _key;
- bytes32 _value;
- }
- struct Map {
- // Storage of map keys and values
- MapEntry[] _entries;
- // Position of the entry defined by a key in the `entries` array, plus 1
- // because index 0 means a key is not in the map.
- mapping (bytes32 => uint256) _indexes;
- }
- /**
- * @dev Adds a key-value pair to a map, or updates the value for an existing
- * key. O(1).
- *
- * Returns true if the key was added to the map, that is if it was not
- * already present.
- */
- function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
- // We read and store the key's index to prevent multiple reads from the same storage slot
- uint256 keyIndex = map._indexes[key];
- if (keyIndex == 0) { // Equivalent to !contains(map, key)
- map._entries.push(MapEntry({ _key: key, _value: value }));
- // The entry is stored at length-1, but we add 1 to all indexes
- // and use 0 as a sentinel value
- map._indexes[key] = map._entries.length;
- return true;
- } else {
- map._entries[keyIndex - 1]._value = value;
- return false;
- }
- }
- /**
- * @dev Removes a key-value pair from a map. O(1).
- *
- * Returns true if the key was removed from the map, that is if it was present.
- */
- function _remove(Map storage map, bytes32 key) private returns (bool) {
- // We read and store the key's index to prevent multiple reads from the same storage slot
- uint256 keyIndex = map._indexes[key];
- if (keyIndex != 0) { // Equivalent to contains(map, key)
- // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
- // in the array, and then remove the last entry (sometimes called as 'swap and pop').
- // This modifies the order of the array, as noted in {at}.
- uint256 toDeleteIndex = keyIndex - 1;
- uint256 lastIndex = map._entries.length - 1;
- // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
- // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
- MapEntry storage lastEntry = map._entries[lastIndex];
- // Move the last entry to the index where the entry to delete is
- map._entries[toDeleteIndex] = lastEntry;
- // Update the index for the moved entry
- map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
- // Delete the slot where the moved entry was stored
- map._entries.pop();
- // Delete the index for the deleted slot
- delete map._indexes[key];
- return true;
- } else {
- return false;
- }
- }
- /**
- * @dev Returns true if the key is in the map. O(1).
- */
- function _contains(Map storage map, bytes32 key) private view returns (bool) {
- return map._indexes[key] != 0;
- }
- /**
- * @dev Returns the number of key-value pairs in the map. O(1).
- */
- function _length(Map storage map) private view returns (uint256) {
- return map._entries.length;
- }
- /**
- * @dev Returns the key-value pair stored at position `index` in the map. O(1).
- *
- * Note that there are no guarantees on the ordering of entries inside the
- * array, and it may change when more entries are added or removed.
- *
- * Requirements:
- *
- * - `index` must be strictly less than {length}.
- */
- function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
- require(map._entries.length > index, "EnumerableMap: index out of bounds");
- MapEntry storage entry = map._entries[index];
- return (entry._key, entry._value);
- }
- /**
- * @dev Returns the value associated with `key`. O(1).
- *
- * Requirements:
- *
- * - `key` must be in the map.
- */
- function _get(Map storage map, bytes32 key) private view returns (bytes32) {
- return _get(map, key, "EnumerableMap: nonexistent key");
- }
- /**
- * @dev Same as {_get}, with a custom error message when `key` is not in the map.
- */
- function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
- uint256 keyIndex = map._indexes[key];
- require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
- return map._entries[keyIndex - 1]._value; // All indexes are 1-based
- }
- // UintToAddressMap
- struct UintToAddressMap {
- Map _inner;
- }
- /**
- * @dev Adds a key-value pair to a map, or updates the value for an existing
- * key. O(1).
- *
- * Returns true if the key was added to the map, that is if it was not
- * already present.
- */
- function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
- return _set(map._inner, bytes32(key), bytes32(uint256(value)));
- }
- /**
- * @dev Removes a value from a set. O(1).
- *
- * Returns true if the key was removed from the map, that is if it was present.
- */
- function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
- return _remove(map._inner, bytes32(key));
- }
- /**
- * @dev Returns true if the key is in the map. O(1).
- */
- function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
- return _contains(map._inner, bytes32(key));
- }
- /**
- * @dev Returns the number of elements in the map. O(1).
- */
- function length(UintToAddressMap storage map) internal view returns (uint256) {
- return _length(map._inner);
- }
- /**
- * @dev Returns the element stored at position `index` in the set. O(1).
- * Note that there are no guarantees on the ordering of values inside the
- * array, and it may change when more values are added or removed.
- *
- * Requirements:
- *
- * - `index` must be strictly less than {length}.
- */
- function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
- (bytes32 key, bytes32 value) = _at(map._inner, index);
- return (uint256(key), address(uint256(value)));
- }
- /**
- * @dev Returns the value associated with `key`. O(1).
- *
- * Requirements:
- *
- * - `key` must be in the map.
- */
- function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
- return address(uint256(_get(map._inner, bytes32(key))));
- }
- /**
- * @dev Same as {get}, with a custom error message when `key` is not in the map.
- */
- function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
- return address(uint256(_get(map._inner, bytes32(key), errorMessage)));
- }
- }
|