Skip to content

Commit

Permalink
✨ Add indexOf and lastIndexOf to DynamicArrayLib (#1096)
Browse files Browse the repository at this point in the history
Co-authored-by: atarpara <[email protected]>
  • Loading branch information
Vectorized and atarpara authored Oct 1, 2024
1 parent d417334 commit fe23e6b
Show file tree
Hide file tree
Showing 2 changed files with 417 additions and 25 deletions.
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

0 comments on commit fe23e6b

Please sign in to comment.