Add paginated version of EnumerableSet.values() and EnumerableMap.keys() (#5713)

Co-authored-by: ernestognw <ernestognw@gmail.com>
This commit is contained in:
Hadrien Croubois
2025-06-04 09:33:00 +02:00
committed by GitHub
parent f45e9158b7
commit d20b9e30bd
10 changed files with 472 additions and 4 deletions

View File

@ -165,7 +165,7 @@ function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns
}
/**
* @dev Return the an array containing all the keys
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
@ -175,6 +175,18 @@ function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
return map._keys.values(start, end);
}
`;
const customMap = ({ name, key, value }) => `\
@ -280,6 +292,25 @@ function keys(${name} storage map) internal view returns (${key.type}[] memory)
return result;
}
/**
* @dev Return the an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(${name} storage map, uint256 start, uint256 end) internal view returns (${key.type}[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
${key.type}[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
`;
const memoryMap = ({ name, keySet, key, value }) => `\
@ -390,7 +421,7 @@ function get(${name} storage map, ${key.typeLoc} key) internal view returns (${v
}
/**
* @dev Return the an array containing all the keys
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
@ -400,6 +431,18 @@ function get(${name} storage map, ${key.typeLoc} key) internal view returns (${v
function keys(${name} storage map) internal view returns (${key.type}[] memory) {
return map._keys.values();
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(${name} storage map, uint256 start, uint256 end) internal view returns (${key.type}[] memory) {
return map._keys.values(start, end);
}
`;
// GENERATE

View File

@ -6,6 +6,7 @@ const header = `\
pragma solidity ^0.8.20;
import {Arrays} from "../Arrays.sol";
import {Math} from "../math/Math.sol";
/**
* @dev Library for managing
@ -179,6 +180,28 @@ function _at(Set storage set, uint256 index) private view returns (bytes32) {
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
unchecked {
end = Math.min(end, _length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes32[] memory result = new bytes32[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
`;
// NOTE: this should be deprecated in favor of a more native construction in v6.0
@ -265,6 +288,25 @@ function values(${name} storage set) internal view returns (${type}[] memory) {
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(${name} storage set, uint256 start, uint256 end) internal view returns (${type}[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
${type}[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
`;
const memorySet = ({ name, value }) => `\
@ -387,6 +429,28 @@ function at(${name} storage self, uint256 index) internal view returns (${value.
function values(${name} storage self) internal view returns (${value.type}[] memory) {
return self._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(${name} storage set, uint256 start, uint256 end) internal view returns (${value.type}[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
${value.type}[] memory result = new ${value.type}[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
`;
// GENERATE