Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add indexOf and lastIndexOf to DynamicArrayLib #1096

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 233 additions & 25 deletions src/utils/DynamicArrayLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ library DynamicArrayLib {
uint256[] data;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @dev The constant returned when the element is not found in the array.
uint256 internal constant NOT_FOUND = type(uint256).max;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UINT256 ARRAY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down Expand Up @@ -97,6 +104,107 @@ library DynamicArrayLib {
}
}

/// @dev Returns a copy of `array` sliced from `start` to `end` (exclusive).
function slice(uint256[] memory array, uint256 start, uint256 end)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let arrayLen := mload(array)
if iszero(gt(arrayLen, end)) { end := arrayLen }
if iszero(gt(arrayLen, start)) { start := arrayLen }
if lt(start, end) {
result := mload(0x40)
let resultLen := sub(end, start)
mstore(result, resultLen)
array := add(array, shl(5, start))
// Copy the `array` one word at a time, backwards.
let o := add(shl(5, resultLen), 0x20)
mstore(0x40, add(result, o)) // Allocate memory.
for {} 1 {} {
mstore(add(result, o), mload(add(array, o)))
o := sub(o, 0x20)
if iszero(o) { break }
}
}
}
}

/// @dev Returns if `needle` is in `array`.
function contains(uint256[] memory array, uint256 needle) internal pure returns (bool) {
return ~indexOf(array, needle, 0) != 0;
}

/// @dev Returns the first index of `needle`, scanning forward from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(uint256[] memory array, uint256 needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0)
if lt(from, mload(array)) {
let o := add(array, shl(5, from))
let end := add(shl(5, add(1, mload(array))), array)
let c := mload(end) // Cache the word after the array.
for { mstore(end, needle) } 1 {} {
o := add(o, 0x20)
if eq(mload(o), needle) { break }
}
mstore(end, c) // Restore the word after the array.
if iszero(eq(o, end)) { result := shr(5, sub(o, add(0x20, array))) }
}
}
}

/// @dev Returns the first index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(uint256[] memory array, uint256 needle)
internal
pure
returns (uint256 result)
{
result = indexOf(array, needle, 0);
}

/// @dev Returns the last index of `needle`, scanning backwards from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(uint256[] memory array, uint256 needle, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := not(0)
let n := mload(array)
if n {
if iszero(lt(from, n)) { from := sub(n, 1) }
let o := add(shl(5, add(2, from)), array)
for { mstore(array, needle) } 1 {} {
o := sub(o, 0x20)
if eq(mload(o), needle) { break }
}
mstore(array, n) // Restore the length of the array.
if iszero(eq(o, array)) { result := shr(5, sub(o, add(0x20, array))) }
}
}
}

/// @dev Returns the first index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(uint256[] memory array, uint256 needle)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(array, needle, NOT_FOUND);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DYNAMIC ARRAY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down Expand Up @@ -574,29 +682,7 @@ library DynamicArrayLib {
pure
returns (DynamicArray memory result)
{
/// @solidity memory-safe-assembly
assembly {
let arrData := mload(array)
let arrDataLen := mload(arrData)
if iszero(gt(arrDataLen, end)) { end := arrDataLen }
if iszero(gt(arrDataLen, start)) { start := arrDataLen }
if lt(start, end) {
let resultData := mload(0x40)
let resultDataLen := sub(end, start)
mstore(resultData, resultDataLen)
arrData := add(arrData, shl(5, start))
let w := not(0x1f)
// Copy the `arrData` one word at a time, backwards.
let o := add(shl(5, resultDataLen), 0x20)
mstore(0x40, add(resultData, o)) // Allocate memory.
for {} 1 {} {
mstore(add(resultData, o), mload(add(arrData, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
mstore(result, resultData)
}
}
result.data = slice(array.data, start, end);
}

/// @dev Returns a copy of `array` sliced from `start` to the end of the array.
Expand All @@ -605,8 +691,130 @@ library DynamicArrayLib {
pure
returns (DynamicArray memory result)
{
_deallocate(result);
result = slice(array, start, type(uint256).max);
result.data = slice(array.data, start, type(uint256).max);
}

/// @dev Returns if `needle` is in `array`.
function contains(DynamicArray memory array, uint256 needle) internal pure returns (bool) {
return ~indexOf(array.data, needle, 0) != 0;
}

/// @dev Returns if `needle` is in `array`.
function contains(DynamicArray memory array, address needle) internal pure returns (bool) {
return ~indexOf(array.data, uint160(needle), 0) != 0;
}

/// @dev Returns if `needle` is in `array`.
function contains(DynamicArray memory array, bytes32 needle) internal pure returns (bool) {
return ~indexOf(array.data, uint256(needle), 0) != 0;
}

/// @dev Returns the first index of `needle`, scanning forward from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(DynamicArray memory array, uint256 needle, uint256 from)
internal
pure
returns (uint256)
{
return indexOf(array.data, needle, from);
}

/// @dev Returns the first index of `needle`, scanning forward from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(DynamicArray memory array, address needle, uint256 from)
internal
pure
returns (uint256)
{
return indexOf(array.data, uint160(needle), from);
}

/// @dev Returns the first index of `needle`, scanning forward from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(DynamicArray memory array, bytes32 needle, uint256 from)
internal
pure
returns (uint256)
{
return indexOf(array.data, uint256(needle), from);
}

/// @dev Returns the first index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(DynamicArray memory array, uint256 needle) internal pure returns (uint256) {
return indexOf(array.data, needle, 0);
}

/// @dev Returns the first index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(DynamicArray memory array, address needle) internal pure returns (uint256) {
return indexOf(array.data, uint160(needle), 0);
}

/// @dev Returns the first index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function indexOf(DynamicArray memory array, bytes32 needle) internal pure returns (uint256) {
return indexOf(array.data, uint256(needle), 0);
}

/// @dev Returns the last index of `needle`, scanning backwards from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(DynamicArray memory array, uint256 needle, uint256 from)
internal
pure
returns (uint256)
{
return lastIndexOf(array.data, needle, from);
}

/// @dev Returns the last index of `needle`, scanning backwards from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(DynamicArray memory array, address needle, uint256 from)
internal
pure
returns (uint256)
{
return lastIndexOf(array.data, uint160(needle), from);
}

/// @dev Returns the last index of `needle`, scanning backwards from `from`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(DynamicArray memory array, bytes32 needle, uint256 from)
internal
pure
returns (uint256)
{
return lastIndexOf(array.data, uint256(needle), from);
}

/// @dev Returns the last index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(DynamicArray memory array, uint256 needle)
internal
pure
returns (uint256)
{
return lastIndexOf(array.data, needle, NOT_FOUND);
}

/// @dev Returns the last index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(DynamicArray memory array, address needle)
internal
pure
returns (uint256)
{
return lastIndexOf(array.data, uint160(needle), NOT_FOUND);
}

/// @dev Returns the last index of `needle`.
/// If `needle` is not in `array`, returns `NOT_FOUND`.
function lastIndexOf(DynamicArray memory array, bytes32 needle)
internal
pure
returns (uint256)
{
return lastIndexOf(array.data, uint256(needle), NOT_FOUND);
}

/// @dev Equivalent to `keccak256(abi.encodePacked(array.data))`.
Expand Down
Loading