Add paginated version of EnumerableSet.values() and EnumerableMap.keys() (#5713)
Co-authored-by: ernestognw <ernestognw@gmail.com>
This commit is contained in:
5
.changeset/fine-frogs-bake.md
Normal file
5
.changeset/fine-frogs-bake.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`EnumerableMap`: Add `keys(uint256,uint256)` that returns a subset (slice) of the keys in the map.
|
||||
5
.changeset/hot-grapes-lie.md
Normal file
5
.changeset/hot-grapes-lie.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'openzeppelin-solidity': minor
|
||||
---
|
||||
|
||||
`EnumerableSet`: Add `values(uint256,uint256)` that returns a subset (slice) of the values in the set.
|
||||
@ -164,7 +164,7 @@ library EnumerableMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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,22 @@ library EnumerableMap {
|
||||
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);
|
||||
}
|
||||
|
||||
// UintToUintMap
|
||||
|
||||
struct UintToUintMap {
|
||||
@ -278,6 +294,25 @@ library EnumerableMap {
|
||||
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(UintToUintMap storage map, uint256 start, uint256 end) internal view returns (uint256[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
uint256[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// UintToAddressMap
|
||||
|
||||
struct UintToAddressMap {
|
||||
@ -381,6 +416,25 @@ library EnumerableMap {
|
||||
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(UintToAddressMap storage map, uint256 start, uint256 end) internal view returns (uint256[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
uint256[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// UintToBytes32Map
|
||||
|
||||
struct UintToBytes32Map {
|
||||
@ -484,6 +538,25 @@ library EnumerableMap {
|
||||
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(UintToBytes32Map storage map, uint256 start, uint256 end) internal view returns (uint256[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
uint256[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddressToUintMap
|
||||
|
||||
struct AddressToUintMap {
|
||||
@ -587,6 +660,25 @@ library EnumerableMap {
|
||||
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(AddressToUintMap storage map, uint256 start, uint256 end) internal view returns (address[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
address[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddressToAddressMap
|
||||
|
||||
struct AddressToAddressMap {
|
||||
@ -690,6 +782,29 @@ library EnumerableMap {
|
||||
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(
|
||||
AddressToAddressMap storage map,
|
||||
uint256 start,
|
||||
uint256 end
|
||||
) internal view returns (address[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
address[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddressToBytes32Map
|
||||
|
||||
struct AddressToBytes32Map {
|
||||
@ -793,6 +908,29 @@ library EnumerableMap {
|
||||
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(
|
||||
AddressToBytes32Map storage map,
|
||||
uint256 start,
|
||||
uint256 end
|
||||
) internal view returns (address[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
address[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Bytes32ToUintMap
|
||||
|
||||
struct Bytes32ToUintMap {
|
||||
@ -896,6 +1034,25 @@ library EnumerableMap {
|
||||
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(Bytes32ToUintMap storage map, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
bytes32[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Bytes32ToAddressMap
|
||||
|
||||
struct Bytes32ToAddressMap {
|
||||
@ -999,6 +1156,29 @@ library EnumerableMap {
|
||||
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(
|
||||
Bytes32ToAddressMap storage map,
|
||||
uint256 start,
|
||||
uint256 end
|
||||
) internal view returns (bytes32[] memory) {
|
||||
bytes32[] memory store = keys(map._inner, start, end);
|
||||
bytes32[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Query for a nonexistent map key.
|
||||
*/
|
||||
@ -1106,7 +1286,7 @@ library EnumerableMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
@ -1116,4 +1296,16 @@ library EnumerableMap {
|
||||
function keys(BytesToBytesMap storage map) internal view returns (bytes[] 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(BytesToBytesMap storage map, uint256 start, uint256 end) internal view returns (bytes[] memory) {
|
||||
return map._keys.values(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Arrays} from "../Arrays.sol";
|
||||
import {Math} from "../math/Math.sol";
|
||||
|
||||
/**
|
||||
* @dev Library for managing
|
||||
@ -176,6 +177,28 @@ library EnumerableSet {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes32Set
|
||||
|
||||
struct Bytes32Set {
|
||||
@ -259,6 +282,25 @@ library EnumerableSet {
|
||||
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(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
|
||||
bytes32[] memory store = _values(set._inner, start, end);
|
||||
bytes32[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddressSet
|
||||
|
||||
struct AddressSet {
|
||||
@ -342,6 +384,25 @@ library EnumerableSet {
|
||||
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(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
|
||||
bytes32[] memory store = _values(set._inner, start, end);
|
||||
address[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// UintSet
|
||||
|
||||
struct UintSet {
|
||||
@ -425,6 +486,25 @@ library EnumerableSet {
|
||||
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(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
|
||||
bytes32[] memory store = _values(set._inner, start, end);
|
||||
uint256[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct StringSet {
|
||||
// Storage of set values
|
||||
string[] _values;
|
||||
@ -545,6 +625,28 @@ library EnumerableSet {
|
||||
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(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
|
||||
unchecked {
|
||||
end = Math.min(end, length(set));
|
||||
start = Math.min(start, end);
|
||||
|
||||
uint256 len = end - start;
|
||||
string[] memory result = new string[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
struct BytesSet {
|
||||
// Storage of set values
|
||||
bytes[] _values;
|
||||
@ -664,4 +766,26 @@ library EnumerableSet {
|
||||
function values(BytesSet storage self) internal view returns (bytes[] 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(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
|
||||
unchecked {
|
||||
end = Math.min(end, length(set));
|
||||
start = Math.min(start, end);
|
||||
|
||||
uint256 len = end - start;
|
||||
bytes[] memory result = new bytes[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -191,6 +191,22 @@ function shouldBehaveLikeMap() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('keys (full & paginated)', async function () {
|
||||
const keys = [this.keyA, this.keyB, this.keyC];
|
||||
await this.methods.set(this.keyA, this.valueA);
|
||||
await this.methods.set(this.keyB, this.valueB);
|
||||
await this.methods.set(this.keyC, this.valueC);
|
||||
|
||||
// get all values
|
||||
expect([...(await this.methods.keys())]).to.deep.equal(keys);
|
||||
|
||||
// try pagination
|
||||
for (const begin of [0, 1, 2, 3, 4])
|
||||
for (const end of [0, 1, 2, 3, 4]) {
|
||||
expect([...(await this.methods.keysPage(begin, end))]).to.deep.equal(keys.slice(begin, end));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -34,6 +34,7 @@ async function fixture() {
|
||||
length: `$length_EnumerableMap_${name}(uint256)`,
|
||||
at: `$at_EnumerableMap_${name}(uint256,uint256)`,
|
||||
keys: `$keys_EnumerableMap_${name}(uint256)`,
|
||||
keysPage: `$keys_EnumerableMap_${name}(uint256,uint256,uint256)`,
|
||||
}
|
||||
: {
|
||||
set: `$set(uint256,${key.type},${value.type})`,
|
||||
@ -45,6 +46,7 @@ async function fixture() {
|
||||
length: `$length_EnumerableMap_${name}(uint256)`,
|
||||
at: `$at_EnumerableMap_${name}(uint256,uint256)`,
|
||||
keys: `$keys_EnumerableMap_${name}(uint256)`,
|
||||
keysPage: `$keys_EnumerableMap_${name}(uint256,uint256,uint256)`,
|
||||
},
|
||||
fnSig =>
|
||||
(...args) =>
|
||||
|
||||
@ -152,6 +152,22 @@ function shouldBehaveLikeSet() {
|
||||
await expectMembersMatch(this.methods, [this.valueA]);
|
||||
});
|
||||
});
|
||||
|
||||
it('values (full & paginated)', async function () {
|
||||
const values = [this.valueA, this.valueB, this.valueC];
|
||||
await this.methods.add(this.valueA);
|
||||
await this.methods.add(this.valueB);
|
||||
await this.methods.add(this.valueC);
|
||||
|
||||
// get all values
|
||||
expect([...(await this.methods.values())]).to.deep.equal(values);
|
||||
|
||||
// try pagination
|
||||
for (const begin of [0, 1, 2, 3, 4])
|
||||
for (const end of [0, 1, 2, 3, 4]) {
|
||||
expect([...(await this.methods.valuesPage(begin, end))]).to.deep.equal(values.slice(begin, end));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@ -35,6 +35,7 @@ async function fixture() {
|
||||
length: `$length_EnumerableSet_${name}(uint256)`,
|
||||
at: `$at_EnumerableSet_${name}(uint256,uint256)`,
|
||||
values: `$values_EnumerableSet_${name}(uint256)`,
|
||||
valuesPage: `$values_EnumerableSet_${name}(uint256,uint256,uint256)`,
|
||||
}),
|
||||
events: {
|
||||
addReturn: `return$add_EnumerableSet_${name}_${value.type.replace(/[[\]]/g, '_')}`,
|
||||
|
||||
Reference in New Issue
Block a user