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
|
* 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
|
* 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();
|
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
|
// UintToUintMap
|
||||||
|
|
||||||
struct UintToUintMap {
|
struct UintToUintMap {
|
||||||
@ -278,6 +294,25 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// UintToAddressMap
|
||||||
|
|
||||||
struct UintToAddressMap {
|
struct UintToAddressMap {
|
||||||
@ -381,6 +416,25 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// UintToBytes32Map
|
||||||
|
|
||||||
struct UintToBytes32Map {
|
struct UintToBytes32Map {
|
||||||
@ -484,6 +538,25 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// AddressToUintMap
|
||||||
|
|
||||||
struct AddressToUintMap {
|
struct AddressToUintMap {
|
||||||
@ -587,6 +660,25 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// AddressToAddressMap
|
||||||
|
|
||||||
struct AddressToAddressMap {
|
struct AddressToAddressMap {
|
||||||
@ -690,6 +782,29 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// AddressToBytes32Map
|
||||||
|
|
||||||
struct AddressToBytes32Map {
|
struct AddressToBytes32Map {
|
||||||
@ -793,6 +908,29 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// Bytes32ToUintMap
|
||||||
|
|
||||||
struct Bytes32ToUintMap {
|
struct Bytes32ToUintMap {
|
||||||
@ -896,6 +1034,25 @@ library EnumerableMap {
|
|||||||
return result;
|
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
|
// Bytes32ToAddressMap
|
||||||
|
|
||||||
struct Bytes32ToAddressMap {
|
struct Bytes32ToAddressMap {
|
||||||
@ -999,6 +1156,29 @@ library EnumerableMap {
|
|||||||
return result;
|
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.
|
* @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
|
* 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
|
* 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) {
|
function keys(BytesToBytesMap storage map) internal view returns (bytes[] memory) {
|
||||||
return map._keys.values();
|
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;
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
import {Arrays} from "../Arrays.sol";
|
import {Arrays} from "../Arrays.sol";
|
||||||
|
import {Math} from "../math/Math.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Library for managing
|
* @dev Library for managing
|
||||||
@ -176,6 +177,28 @@ library EnumerableSet {
|
|||||||
return set._values;
|
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
|
// Bytes32Set
|
||||||
|
|
||||||
struct Bytes32Set {
|
struct Bytes32Set {
|
||||||
@ -259,6 +282,25 @@ library EnumerableSet {
|
|||||||
return result;
|
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
|
// AddressSet
|
||||||
|
|
||||||
struct AddressSet {
|
struct AddressSet {
|
||||||
@ -342,6 +384,25 @@ library EnumerableSet {
|
|||||||
return result;
|
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
|
// UintSet
|
||||||
|
|
||||||
struct UintSet {
|
struct UintSet {
|
||||||
@ -425,6 +486,25 @@ library EnumerableSet {
|
|||||||
return result;
|
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 {
|
struct StringSet {
|
||||||
// Storage of set values
|
// Storage of set values
|
||||||
string[] _values;
|
string[] _values;
|
||||||
@ -545,6 +625,28 @@ library EnumerableSet {
|
|||||||
return self._values;
|
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 {
|
struct BytesSet {
|
||||||
// Storage of set values
|
// Storage of set values
|
||||||
bytes[] _values;
|
bytes[] _values;
|
||||||
@ -664,4 +766,26 @@ library EnumerableSet {
|
|||||||
function values(BytesSet storage self) internal view returns (bytes[] memory) {
|
function values(BytesSet storage self) internal view returns (bytes[] memory) {
|
||||||
return self._values;
|
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
|
* 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
|
* 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) {
|
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
|
||||||
return map._keys.values();
|
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 }) => `\
|
const customMap = ({ name, key, value }) => `\
|
||||||
@ -280,6 +292,25 @@ function keys(${name} storage map) internal view returns (${key.type}[] memory)
|
|||||||
|
|
||||||
return result;
|
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 }) => `\
|
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
|
* 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
|
* 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) {
|
function keys(${name} storage map) internal view returns (${key.type}[] memory) {
|
||||||
return map._keys.values();
|
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
|
// GENERATE
|
||||||
|
|||||||
@ -6,6 +6,7 @@ const header = `\
|
|||||||
pragma solidity ^0.8.20;
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
import {Arrays} from "../Arrays.sol";
|
import {Arrays} from "../Arrays.sol";
|
||||||
|
import {Math} from "../math/Math.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Library for managing
|
* @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) {
|
function _values(Set storage set) private view returns (bytes32[] memory) {
|
||||||
return set._values;
|
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
|
// 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;
|
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 }) => `\
|
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) {
|
function values(${name} storage self) internal view returns (${value.type}[] memory) {
|
||||||
return self._values;
|
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
|
// 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 = {
|
module.exports = {
|
||||||
|
|||||||
@ -34,6 +34,7 @@ async function fixture() {
|
|||||||
length: `$length_EnumerableMap_${name}(uint256)`,
|
length: `$length_EnumerableMap_${name}(uint256)`,
|
||||||
at: `$at_EnumerableMap_${name}(uint256,uint256)`,
|
at: `$at_EnumerableMap_${name}(uint256,uint256)`,
|
||||||
keys: `$keys_EnumerableMap_${name}(uint256)`,
|
keys: `$keys_EnumerableMap_${name}(uint256)`,
|
||||||
|
keysPage: `$keys_EnumerableMap_${name}(uint256,uint256,uint256)`,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
set: `$set(uint256,${key.type},${value.type})`,
|
set: `$set(uint256,${key.type},${value.type})`,
|
||||||
@ -45,6 +46,7 @@ async function fixture() {
|
|||||||
length: `$length_EnumerableMap_${name}(uint256)`,
|
length: `$length_EnumerableMap_${name}(uint256)`,
|
||||||
at: `$at_EnumerableMap_${name}(uint256,uint256)`,
|
at: `$at_EnumerableMap_${name}(uint256,uint256)`,
|
||||||
keys: `$keys_EnumerableMap_${name}(uint256)`,
|
keys: `$keys_EnumerableMap_${name}(uint256)`,
|
||||||
|
keysPage: `$keys_EnumerableMap_${name}(uint256,uint256,uint256)`,
|
||||||
},
|
},
|
||||||
fnSig =>
|
fnSig =>
|
||||||
(...args) =>
|
(...args) =>
|
||||||
|
|||||||
@ -152,6 +152,22 @@ function shouldBehaveLikeSet() {
|
|||||||
await expectMembersMatch(this.methods, [this.valueA]);
|
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 = {
|
module.exports = {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ async function fixture() {
|
|||||||
length: `$length_EnumerableSet_${name}(uint256)`,
|
length: `$length_EnumerableSet_${name}(uint256)`,
|
||||||
at: `$at_EnumerableSet_${name}(uint256,uint256)`,
|
at: `$at_EnumerableSet_${name}(uint256,uint256)`,
|
||||||
values: `$values_EnumerableSet_${name}(uint256)`,
|
values: `$values_EnumerableSet_${name}(uint256)`,
|
||||||
|
valuesPage: `$values_EnumerableSet_${name}(uint256,uint256,uint256)`,
|
||||||
}),
|
}),
|
||||||
events: {
|
events: {
|
||||||
addReturn: `return$add_EnumerableSet_${name}_${value.type.replace(/[[\]]/g, '_')}`,
|
addReturn: `return$add_EnumerableSet_${name}_${value.type.replace(/[[\]]/g, '_')}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user