Skip to content

Commit 784d4f7

Browse files
ernestognwAmxx
andauthored
Add non-value types in EnumerableSet and EnumerableMap (#5658)
Co-authored-by: Hadrien Croubois <[email protected]>
1 parent 4bafedf commit 784d4f7

File tree

13 files changed

+770
-101
lines changed

13 files changed

+770
-101
lines changed

.changeset/long-hornets-mate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`EnumerableMap`: Add support for `BytesToBytesMap` type.

.changeset/pink-dolls-shop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`EnumerableSet`: Add support for `StringSet` and `BytesSet` types.

contracts/utils/structs/EnumerableMap.sol

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {EnumerableSet} from "./EnumerableSet.sol";
3939
* - `address -> address` (`AddressToAddressMap`) since v5.1.0
4040
* - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0
4141
* - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0
42+
* - `bytes -> bytes` (`BytesToBytesMap`) since v5.4.0
4243
*
4344
* [WARNING]
4445
* ====
@@ -51,7 +52,7 @@ import {EnumerableSet} from "./EnumerableSet.sol";
5152
* ====
5253
*/
5354
library EnumerableMap {
54-
using EnumerableSet for EnumerableSet.Bytes32Set;
55+
using EnumerableSet for *;
5556

5657
// To implement this library for multiple types with as little code repetition as possible, we write it in
5758
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
@@ -997,4 +998,122 @@ library EnumerableMap {
997998

998999
return result;
9991000
}
1001+
1002+
/**
1003+
* @dev Query for a nonexistent map key.
1004+
*/
1005+
error EnumerableMapNonexistentBytesKey(bytes key);
1006+
1007+
struct BytesToBytesMap {
1008+
// Storage of keys
1009+
EnumerableSet.BytesSet _keys;
1010+
mapping(bytes key => bytes) _values;
1011+
}
1012+
1013+
/**
1014+
* @dev Adds a key-value pair to a map, or updates the value for an existing
1015+
* key. O(1).
1016+
*
1017+
* Returns true if the key was added to the map, that is if it was not
1018+
* already present.
1019+
*/
1020+
function set(BytesToBytesMap storage map, bytes memory key, bytes memory value) internal returns (bool) {
1021+
map._values[key] = value;
1022+
return map._keys.add(key);
1023+
}
1024+
1025+
/**
1026+
* @dev Removes a key-value pair from a map. O(1).
1027+
*
1028+
* Returns true if the key was removed from the map, that is if it was present.
1029+
*/
1030+
function remove(BytesToBytesMap storage map, bytes memory key) internal returns (bool) {
1031+
delete map._values[key];
1032+
return map._keys.remove(key);
1033+
}
1034+
1035+
/**
1036+
* @dev Removes all the entries from a map. O(n).
1037+
*
1038+
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
1039+
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
1040+
*/
1041+
function clear(BytesToBytesMap storage map) internal {
1042+
uint256 len = length(map);
1043+
for (uint256 i = 0; i < len; ++i) {
1044+
delete map._values[map._keys.at(i)];
1045+
}
1046+
map._keys.clear();
1047+
}
1048+
1049+
/**
1050+
* @dev Returns true if the key is in the map. O(1).
1051+
*/
1052+
function contains(BytesToBytesMap storage map, bytes memory key) internal view returns (bool) {
1053+
return map._keys.contains(key);
1054+
}
1055+
1056+
/**
1057+
* @dev Returns the number of key-value pairs in the map. O(1).
1058+
*/
1059+
function length(BytesToBytesMap storage map) internal view returns (uint256) {
1060+
return map._keys.length();
1061+
}
1062+
1063+
/**
1064+
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
1065+
*
1066+
* Note that there are no guarantees on the ordering of entries inside the
1067+
* array, and it may change when more entries are added or removed.
1068+
*
1069+
* Requirements:
1070+
*
1071+
* - `index` must be strictly less than {length}.
1072+
*/
1073+
function at(
1074+
BytesToBytesMap storage map,
1075+
uint256 index
1076+
) internal view returns (bytes memory key, bytes memory value) {
1077+
key = map._keys.at(index);
1078+
value = map._values[key];
1079+
}
1080+
1081+
/**
1082+
* @dev Tries to returns the value associated with `key`. O(1).
1083+
* Does not revert if `key` is not in the map.
1084+
*/
1085+
function tryGet(
1086+
BytesToBytesMap storage map,
1087+
bytes memory key
1088+
) internal view returns (bool exists, bytes memory value) {
1089+
value = map._values[key];
1090+
exists = bytes(value).length != 0 || contains(map, key);
1091+
}
1092+
1093+
/**
1094+
* @dev Returns the value associated with `key`. O(1).
1095+
*
1096+
* Requirements:
1097+
*
1098+
* - `key` must be in the map.
1099+
*/
1100+
function get(BytesToBytesMap storage map, bytes memory key) internal view returns (bytes memory value) {
1101+
bool exists;
1102+
(exists, value) = tryGet(map, key);
1103+
if (!exists) {
1104+
revert EnumerableMapNonexistentBytesKey(key);
1105+
}
1106+
}
1107+
1108+
/**
1109+
* @dev Return the an array containing all the keys
1110+
*
1111+
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
1112+
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
1113+
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
1114+
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
1115+
*/
1116+
function keys(BytesToBytesMap storage map) internal view returns (bytes[] memory) {
1117+
return map._keys.values();
1118+
}
10001119
}

0 commit comments

Comments
 (0)