From 37511a2709937adcfb873263a5ed7d27484070df Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 10:20:28 +0000 Subject: [PATCH 01/11] Add lastIndexOf --- src/utils/DynamicArrayLib.sol | 160 ++++++++++++++++++++++++++++------ test/DynamicArrayLib.t.sol | 105 ++++++++++++++++++++++ 2 files changed, 240 insertions(+), 25 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 36a0c8681..61bc0c570 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -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 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ @@ -97,6 +104,84 @@ 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)) + let w := not(0x1f) + // 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 := add(o, w) // `sub(o, 0x20)`. + if iszero(o) { break } + } + } + } + } + + /// @dev Returns the first index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function indexOf(uint256[] memory array, uint256 needle) + internal + pure + returns (uint256 result) + { + /// @solidity memory-safe-assembly + assembly { + result := not(0) + if mload(array) { + let o := array + let end := add(shl(5, add(1, mload(o))), o) + let c := mload(end) // Cache the word after the array. + mstore(end, needle) + for {} 1 {} { + o := add(o, 0x20) + if eq(mload(o), needle) { break } + } + mstore(end, c) // Restore the word after the array. + result := or(sub(0, eq(o, end)), shr(5, sub(o, add(0x20, array)))) + } + } + } + + /// @dev Returns the last index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function lastIndexOf(uint256[] memory array, uint256 needle) + internal + pure + returns (uint256 result) + { + /// @solidity memory-safe-assembly + assembly { + result := not(0) + if mload(array) { + let n := mload(array) + let o := add(shl(5, add(1, n)), array) + mstore(array, needle) + for {} 1 {} { + o := sub(o, 0x20) + if eq(mload(o), needle) { break } + } + mstore(array, n) // Restore the length of the array. + result := or(sub(0, eq(o, array)), shr(5, sub(o, add(0x20, array)))) + } + } + } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DYNAMIC ARRAY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ @@ -574,29 +659,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. @@ -605,8 +668,55 @@ 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 the first index of `needle` in `array`. + /// 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); + } + + /// @dev Returns the first index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function indexOf(DynamicArray memory array, int256 needle) internal pure returns (uint256) { + return indexOf(array.data, uint256(needle)); + } + + /// @dev Returns the first index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function indexOf(DynamicArray memory array, address needle) internal pure returns (uint256) { + return indexOf(array.data, uint256(uint160(needle))); + } + + /// @dev Returns the last index of `needle` in `array`. + /// 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); + } + + /// @dev Returns the last index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function lastIndexOf(DynamicArray memory array, int256 needle) + internal + pure + returns (uint256) + { + return lastIndexOf(array.data, uint256(needle)); + } + + /// @dev Returns the last index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function lastIndexOf(DynamicArray memory array, address needle) + internal + pure + returns (uint256) + { + return lastIndexOf(array.data, uint256(uint160(needle))); } /// @dev Equivalent to `keccak256(abi.encodePacked(array.data))`. diff --git a/test/DynamicArrayLib.t.sol b/test/DynamicArrayLib.t.sol index c8f6a8434..df6a99c1b 100644 --- a/test/DynamicArrayLib.t.sol +++ b/test/DynamicArrayLib.t.sol @@ -214,6 +214,111 @@ contract DynamicArrayLibTest is SoladyTest { } } + function testUint256ArrayIndexOf() public { + uint256 n = 50; + uint256[] memory a; + assertEq(DynamicArrayLib.indexOf(a, 0), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.indexOf(a, 1), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.indexOf(a, 2), DynamicArrayLib.NOT_FOUND); + a = new uint256[](0); + assertEq(DynamicArrayLib.indexOf(a, 0), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.indexOf(a, 1), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.indexOf(a, 2), DynamicArrayLib.NOT_FOUND); + a = new uint256[](1); + assertEq(DynamicArrayLib.indexOf(a, 0), 0); + assertEq(DynamicArrayLib.indexOf(a, 1), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.indexOf(a, 2), DynamicArrayLib.NOT_FOUND); + a = DynamicArrayLib.malloc(n); + unchecked { + for (uint256 i; i != n; ++i) { + a.set(i, i); + } + } + assertEq(DynamicArrayLib.indexOf(a, 0), 0); + assertEq(DynamicArrayLib.indexOf(a, 1), 1); + assertEq(DynamicArrayLib.indexOf(a, 10), 10); + assertEq(DynamicArrayLib.indexOf(a, 31), 31); + assertEq(DynamicArrayLib.indexOf(a, 32), 32); + assertEq(DynamicArrayLib.indexOf(a, 49), 49); + assertEq(DynamicArrayLib.indexOf(a, 50), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.indexOf(a, 100), DynamicArrayLib.NOT_FOUND); + } + + function testUint256ArrayIndexOfDifferential(uint256[] memory array, uint256 needle) public { + if (_randomChance(2)) _misalignFreeMemoryPointer(); + if (_randomChance(8)) _brutalizeMemory(); + uint256 computed = DynamicArrayLib.indexOf(array, needle); + assertEq(computed, _indexOfOriginal(array, needle)); + } + + function _indexOfOriginal(uint256[] memory array, uint256 needle) + internal + pure + returns (uint256) + { + unchecked { + uint256 n = array.length; + for (uint256 i; i != n; ++i) { + if (array[i] == needle) return i; + } + } + return type(uint256).max; + } + + function testUint256ArrayLastIndexOf() public { + uint256 n = 50; + uint256[] memory a; + assertEq(DynamicArrayLib.lastIndexOf(a, 0), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 1), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 2), DynamicArrayLib.NOT_FOUND); + a = new uint256[](0); + assertEq(DynamicArrayLib.lastIndexOf(a, 0), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 1), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 2), DynamicArrayLib.NOT_FOUND); + a = new uint256[](1); + assertEq(DynamicArrayLib.lastIndexOf(a, 0), 0); + assertEq(DynamicArrayLib.lastIndexOf(a, 1), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 2), DynamicArrayLib.NOT_FOUND); + a = DynamicArrayLib.malloc(n); + unchecked { + for (uint256 i; i != n; ++i) { + a.set(i, i); + } + } + assertEq(DynamicArrayLib.lastIndexOf(a, 0), 0); + assertEq(DynamicArrayLib.lastIndexOf(a, 1), 1); + assertEq(DynamicArrayLib.lastIndexOf(a, 10), 10); + assertEq(DynamicArrayLib.lastIndexOf(a, 31), 31); + assertEq(DynamicArrayLib.lastIndexOf(a, 32), 32); + assertEq(DynamicArrayLib.lastIndexOf(a, 49), 49); + assertEq(DynamicArrayLib.lastIndexOf(a, 50), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 100), DynamicArrayLib.NOT_FOUND); + } + + function testUint256ArrayLastIndexOfDifferential(uint256[] memory array, uint256 needle) + public + { + if (_randomChance(2)) _misalignFreeMemoryPointer(); + if (_randomChance(8)) _brutalizeMemory(); + uint256 computed = DynamicArrayLib.lastIndexOf(array, needle); + assertEq(computed, _lastIndexOfOriginal(array, needle)); + } + + function _lastIndexOfOriginal(uint256[] memory array, uint256 needle) + internal + pure + returns (uint256) + { + unchecked { + uint256 n = array.length; + for (uint256 i; i != n; ++i) { + uint256 j = n - 1 - i; + if (array[j] == needle) return j; + } + } + return type(uint256).max; + } + function testUint256ArrayPopulate() public { unchecked { uint256 n = 100; From ff6fc95f7861d2d8fc2a9ecff0ff52f0cbdaacc7 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 10:54:54 +0000 Subject: [PATCH 02/11] T --- src/utils/DynamicArrayLib.sol | 115 +++++++++++++++++++++++++++++----- test/DynamicArrayLib.t.sol | 61 ++++++++++++++---- 2 files changed, 148 insertions(+), 28 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 61bc0c570..7bff1b7e9 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -133,9 +133,10 @@ library DynamicArrayLib { } } - /// @dev Returns the first index of `needle` in `array`. + /// @dev Returns the first index of `needle` in `array`, + /// searching from left to right, starting from `from`. /// If `needle` is not in `array`, returns `NOT_FOUND`. - function indexOf(uint256[] memory array, uint256 needle) + function indexOf(uint256[] memory array, uint256 needle, uint256 from) internal pure returns (uint256 result) @@ -143,9 +144,9 @@ library DynamicArrayLib { /// @solidity memory-safe-assembly assembly { result := not(0) - if mload(array) { - let o := array - let end := add(shl(5, add(1, mload(o))), o) + 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. mstore(end, needle) for {} 1 {} { @@ -158,9 +159,20 @@ library DynamicArrayLib { } } - /// @dev Returns the last index of `needle` in `array`. + /// @dev Returns the first index of `needle` in `array`. /// If `needle` is not in `array`, returns `NOT_FOUND`. - function lastIndexOf(uint256[] memory array, uint256 needle) + function indexOf(uint256[] memory array, uint256 needle) + internal + pure + returns (uint256 result) + { + result = indexOf(array, needle, 0); + } + + /// @dev Returns the last index of `needle` in `array`, + /// searching from right to left, starting 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) @@ -168,9 +180,10 @@ library DynamicArrayLib { /// @solidity memory-safe-assembly assembly { result := not(0) - if mload(array) { - let n := mload(array) - let o := add(shl(5, add(1, n)), array) + let n := mload(array) + if gt(from, n) { from := n } + if from { + let o := add(shl(5, add(1, from)), array) mstore(array, needle) for {} 1 {} { o := sub(o, 0x20) @@ -182,6 +195,16 @@ library DynamicArrayLib { } } + /// @dev Returns the first index of `needle` in `array`. + /// 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, uint256(int256(-1))); + } + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DYNAMIC ARRAY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ @@ -671,22 +694,62 @@ library DynamicArrayLib { result.data = slice(array.data, start, type(uint256).max); } + /// @dev Returns the first index of `needle` in `array`. + /// 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` in `array`. /// 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); + return indexOf(array.data, needle, 0); + } + + /// @dev Returns the first index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function indexOf(DynamicArray memory array, int256 needle, uint256 from) + internal + pure + returns (uint256) + { + return indexOf(array.data, uint256(needle), from); } /// @dev Returns the first index of `needle` in `array`. /// If `needle` is not in `array`, returns `NOT_FOUND`. function indexOf(DynamicArray memory array, int256 needle) internal pure returns (uint256) { - return indexOf(array.data, uint256(needle)); + return indexOf(array.data, uint256(needle), 0); + } + + /// @dev Returns the first index of `needle` in `array`. + /// 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, uint256(uint160(needle)), from); } /// @dev Returns the first index of `needle` in `array`. /// If `needle` is not in `array`, returns `NOT_FOUND`. function indexOf(DynamicArray memory array, address needle) internal pure returns (uint256) { - return indexOf(array.data, uint256(uint160(needle))); + return indexOf(array.data, uint256(uint160(needle)), 0); + } + + /// @dev Returns the last index of `needle` in `array`. + /// 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` in `array`. @@ -696,7 +759,17 @@ library DynamicArrayLib { pure returns (uint256) { - return lastIndexOf(array.data, needle); + return lastIndexOf(array.data, needle, uint256(int256(-1))); + } + + /// @dev Returns the last index of `needle` in `array`. + /// If `needle` is not in `array`, returns `NOT_FOUND`. + function lastIndexOf(DynamicArray memory array, int256 needle, uint256 from) + internal + pure + returns (uint256) + { + return lastIndexOf(array.data, uint256(needle), from); } /// @dev Returns the last index of `needle` in `array`. @@ -706,7 +779,17 @@ library DynamicArrayLib { pure returns (uint256) { - return lastIndexOf(array.data, uint256(needle)); + return lastIndexOf(array.data, uint256(needle), uint256(int256(-1))); + } + + /// @dev Returns the last index of `needle` in `array`. + /// 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, uint256(uint160(needle)), from); } /// @dev Returns the last index of `needle` in `array`. @@ -716,7 +799,7 @@ library DynamicArrayLib { pure returns (uint256) { - return lastIndexOf(array.data, uint256(uint160(needle))); + return lastIndexOf(array.data, uint256(uint160(needle)), uint256(int256(-1))); } /// @dev Equivalent to `keccak256(abi.encodePacked(array.data))`. diff --git a/test/DynamicArrayLib.t.sol b/test/DynamicArrayLib.t.sol index df6a99c1b..33da3ed91 100644 --- a/test/DynamicArrayLib.t.sol +++ b/test/DynamicArrayLib.t.sol @@ -244,21 +244,40 @@ contract DynamicArrayLibTest is SoladyTest { assertEq(DynamicArrayLib.indexOf(a, 100), DynamicArrayLib.NOT_FOUND); } - function testUint256ArrayIndexOfDifferential(uint256[] memory array, uint256 needle) public { + function testUint256ArrayIndexOfDifferential( + uint256[] memory array, + uint256 needle, + uint256 from + ) public { if (_randomChance(2)) _misalignFreeMemoryPointer(); if (_randomChance(8)) _brutalizeMemory(); - uint256 computed = DynamicArrayLib.indexOf(array, needle); - assertEq(computed, _indexOfOriginal(array, needle)); + from = _bound(from, 0, array.length + 10); + uint256 computed = DynamicArrayLib.indexOf(array, needle, from); + assertEq(computed, _indexOfOriginal(array, needle, from)); + if (_randomChance(16)) { + computed = DynamicArrayLib.indexOf(array, needle); + assertEq(computed, _indexOfOriginal(array, needle)); + computed = DynamicArrayLib.indexOf(DynamicArrayLib.DynamicArray(array), needle); + assertEq(computed, _indexOfOriginal(array, needle)); + } } function _indexOfOriginal(uint256[] memory array, uint256 needle) internal pure returns (uint256) + { + return _indexOfOriginal(array, needle, 0); + } + + function _indexOfOriginal(uint256[] memory array, uint256 needle, uint256 from) + internal + pure + returns (uint256) { unchecked { uint256 n = array.length; - for (uint256 i; i != n; ++i) { + for (uint256 i = from; i < n; ++i) { if (array[i] == needle) return i; } } @@ -295,25 +314,43 @@ contract DynamicArrayLibTest is SoladyTest { assertEq(DynamicArrayLib.lastIndexOf(a, 100), DynamicArrayLib.NOT_FOUND); } - function testUint256ArrayLastIndexOfDifferential(uint256[] memory array, uint256 needle) - public - { + function testUint256ArrayLastIndexOfDifferential( + uint256[] memory array, + uint256 needle, + uint256 from + ) public { if (_randomChance(2)) _misalignFreeMemoryPointer(); if (_randomChance(8)) _brutalizeMemory(); - uint256 computed = DynamicArrayLib.lastIndexOf(array, needle); - assertEq(computed, _lastIndexOfOriginal(array, needle)); + from = _bound(from, 0, array.length + 10); + uint256 computed = DynamicArrayLib.lastIndexOf(array, needle, from); + assertEq(computed, _lastIndexOfOriginal(array, needle, from)); + if (_randomChance(16)) { + computed = DynamicArrayLib.lastIndexOf(array, needle); + assertEq(computed, _lastIndexOfOriginal(array, needle)); + computed = DynamicArrayLib.lastIndexOf(DynamicArrayLib.DynamicArray(array), needle); + assertEq(computed, _lastIndexOfOriginal(array, needle)); + } } function _lastIndexOfOriginal(uint256[] memory array, uint256 needle) internal pure returns (uint256) + { + return _lastIndexOfOriginal(array, needle, type(uint256).max); + } + + function _lastIndexOfOriginal(uint256[] memory array, uint256 needle, uint256 from) + internal + pure + returns (uint256) { unchecked { uint256 n = array.length; - for (uint256 i; i != n; ++i) { - uint256 j = n - 1 - i; - if (array[j] == needle) return j; + if (from > n) from = n; + for (uint256 i = from; i != 0;) { + --i; + if (array[i] == needle) return i; } } return type(uint256).max; From b228e90c33bffe0b473382228db04fbe0211fc9b Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 11:00:29 +0000 Subject: [PATCH 03/11] T --- src/utils/DynamicArrayLib.sol | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 7bff1b7e9..828ad188d 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -133,6 +133,11 @@ library DynamicArrayLib { } } + /// @dev Returns if `needle` is in `array`. + function contains(uint256[] memory array, uint256 needle) internal pure returns (bool) { + return indexOf(array, needle, 0) != NOT_FOUND; + } + /// @dev Returns the first index of `needle` in `array`, /// searching from left to right, starting from `from`. /// If `needle` is not in `array`, returns `NOT_FOUND`. @@ -694,6 +699,21 @@ library DynamicArrayLib { 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) != NOT_FOUND; + } + + /// @dev Returns if `needle` is in `array`. + function contains(DynamicArray memory array, int256 needle) internal pure returns (bool) { + return indexOf(array.data, uint256(needle), 0) != NOT_FOUND; + } + + /// @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) != NOT_FOUND; + } + /// @dev Returns the first index of `needle` in `array`. /// If `needle` is not in `array`, returns `NOT_FOUND`. function indexOf(DynamicArray memory array, uint256 needle, uint256 from) @@ -733,13 +753,13 @@ library DynamicArrayLib { pure returns (uint256) { - return indexOf(array.data, uint256(uint160(needle)), from); + return indexOf(array.data, uint160(needle), from); } /// @dev Returns the first index of `needle` in `array`. /// If `needle` is not in `array`, returns `NOT_FOUND`. function indexOf(DynamicArray memory array, address needle) internal pure returns (uint256) { - return indexOf(array.data, uint256(uint160(needle)), 0); + return indexOf(array.data, uint160(needle), 0); } /// @dev Returns the last index of `needle` in `array`. @@ -789,7 +809,7 @@ library DynamicArrayLib { pure returns (uint256) { - return lastIndexOf(array.data, uint256(uint160(needle)), from); + return lastIndexOf(array.data, uint160(needle), from); } /// @dev Returns the last index of `needle` in `array`. @@ -799,7 +819,7 @@ library DynamicArrayLib { pure returns (uint256) { - return lastIndexOf(array.data, uint256(uint160(needle)), uint256(int256(-1))); + return lastIndexOf(array.data, uint160(needle), uint256(int256(-1))); } /// @dev Equivalent to `keccak256(abi.encodePacked(array.data))`. From 3920614d6d436ca2287592f76aeaacdd4e312c42 Mon Sep 17 00:00:00 2001 From: atarpara Date: Mon, 30 Sep 2024 21:55:33 +0530 Subject: [PATCH 04/11] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Optimized=20slice()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/DynamicArrayLib.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 828ad188d..7176b3769 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -120,13 +120,12 @@ library DynamicArrayLib { let resultLen := sub(end, start) mstore(result, resultLen) array := add(array, shl(5, start)) - let w := not(0x1f) // 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 := add(o, w) // `sub(o, 0x20)`. + o := sub(o, 0x20) if iszero(o) { break } } } From 744004ebc0d67b5b1daf0b85be5efd5c06140acf Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 18:17:32 +0000 Subject: [PATCH 05/11] T --- src/utils/DynamicArrayLib.sol | 88 +++++++++++++++++------------------ 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 828ad188d..10b5422b8 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -138,8 +138,7 @@ library DynamicArrayLib { return indexOf(array, needle, 0) != NOT_FOUND; } - /// @dev Returns the first index of `needle` in `array`, - /// searching from left to right, starting from `from`. + /// @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 @@ -164,7 +163,7 @@ library DynamicArrayLib { } } - /// @dev Returns the first index of `needle` in `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 @@ -174,8 +173,7 @@ library DynamicArrayLib { result = indexOf(array, needle, 0); } - /// @dev Returns the last index of `needle` in `array`, - /// searching from right to left, starting from `from`. + /// @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 @@ -200,14 +198,14 @@ library DynamicArrayLib { } } - /// @dev Returns the first index of `needle` in `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, uint256(int256(-1))); + result = lastIndexOf(array, needle, NOT_FOUND); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -705,16 +703,16 @@ library DynamicArrayLib { } /// @dev Returns if `needle` is in `array`. - function contains(DynamicArray memory array, int256 needle) internal pure returns (bool) { - return indexOf(array.data, uint256(needle), 0) != NOT_FOUND; + function contains(DynamicArray memory array, address needle) internal pure returns (bool) { + return indexOf(array.data, uint160(needle), 0) != NOT_FOUND; } /// @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) != NOT_FOUND; + function contains(DynamicArray memory array, bytes32 needle) internal pure returns (bool) { + return indexOf(array.data, uint256(needle), 0) != NOT_FOUND; } - /// @dev Returns the first index of `needle` in `array`. + /// @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 @@ -724,15 +722,19 @@ library DynamicArrayLib { return indexOf(array.data, needle, from); } - /// @dev Returns the first index of `needle` in `array`. + /// @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) internal pure returns (uint256) { - return indexOf(array.data, needle, 0); + 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` in `array`. + /// @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, int256 needle, uint256 from) + function indexOf(DynamicArray memory array, bytes32 needle, uint256 from) internal pure returns (uint256) @@ -740,29 +742,25 @@ library DynamicArrayLib { return indexOf(array.data, uint256(needle), from); } - /// @dev Returns the first index of `needle` in `array`. + /// @dev Returns the first index of `needle`. /// If `needle` is not in `array`, returns `NOT_FOUND`. - function indexOf(DynamicArray memory array, int256 needle) internal pure returns (uint256) { - return indexOf(array.data, uint256(needle), 0); + function indexOf(DynamicArray memory array, uint256 needle) internal pure returns (uint256) { + return indexOf(array.data, needle, 0); } - /// @dev Returns the first index of `needle` in `array`. + /// @dev Returns the first index of `needle`. /// 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); + 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` in `array`. + /// @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); + 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` in `array`. + /// @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 @@ -772,19 +770,19 @@ library DynamicArrayLib { return lastIndexOf(array.data, needle, from); } - /// @dev Returns the last index of `needle` in `array`. + /// @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) + function lastIndexOf(DynamicArray memory array, address needle, uint256 from) internal pure returns (uint256) { - return lastIndexOf(array.data, needle, uint256(int256(-1))); + return lastIndexOf(array.data, uint160(needle), from); } - /// @dev Returns the last index of `needle` in `array`. + /// @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, int256 needle, uint256 from) + function lastIndexOf(DynamicArray memory array, bytes32 needle, uint256 from) internal pure returns (uint256) @@ -792,34 +790,34 @@ library DynamicArrayLib { return lastIndexOf(array.data, uint256(needle), from); } - /// @dev Returns the last index of `needle` in `array`. + /// @dev Returns the last index of `needle`. /// If `needle` is not in `array`, returns `NOT_FOUND`. - function lastIndexOf(DynamicArray memory array, int256 needle) + function lastIndexOf(DynamicArray memory array, uint256 needle) internal pure returns (uint256) { - return lastIndexOf(array.data, uint256(needle), uint256(int256(-1))); + return lastIndexOf(array.data, needle, NOT_FOUND); } - /// @dev Returns the last index of `needle` in `array`. + /// @dev Returns the last index of `needle`. /// If `needle` is not in `array`, returns `NOT_FOUND`. - function lastIndexOf(DynamicArray memory array, address needle, uint256 from) + function lastIndexOf(DynamicArray memory array, address needle) internal pure returns (uint256) { - return lastIndexOf(array.data, uint160(needle), from); + return lastIndexOf(array.data, uint160(needle), NOT_FOUND); } - /// @dev Returns the last index of `needle` in `array`. + /// @dev Returns the last index of `needle`. /// If `needle` is not in `array`, returns `NOT_FOUND`. - function lastIndexOf(DynamicArray memory array, address needle) + function lastIndexOf(DynamicArray memory array, bytes32 needle) internal pure returns (uint256) { - return lastIndexOf(array.data, uint160(needle), uint256(int256(-1))); + return lastIndexOf(array.data, uint256(needle), NOT_FOUND); } /// @dev Equivalent to `keccak256(abi.encodePacked(array.data))`. From 24fc304feddfde02291f4d22a404c75d160c0c86 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 18:18:02 +0000 Subject: [PATCH 06/11] T --- src/utils/DynamicArrayLib.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 83d898e11..8b59458b9 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -120,12 +120,13 @@ library DynamicArrayLib { let resultLen := sub(end, start) mstore(result, resultLen) array := add(array, shl(5, start)) + let w := not(0x1f) // 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) + o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } } @@ -797,7 +798,7 @@ library DynamicArrayLib { 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`. From 8f8557aed821d79dfe3c270d9458e5834539184f Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 18:19:46 +0000 Subject: [PATCH 07/11] T --- src/utils/DynamicArrayLib.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 8b59458b9..83d898e11 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -120,13 +120,12 @@ library DynamicArrayLib { let resultLen := sub(end, start) mstore(result, resultLen) array := add(array, shl(5, start)) - let w := not(0x1f) // 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 := add(o, w) // `sub(o, 0x20)`. + o := sub(o, 0x20) if iszero(o) { break } } } @@ -798,7 +797,7 @@ library DynamicArrayLib { 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`. From 8b7b3f2e83c45aeba3f69472b07084ab9c25cd6a Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 18:28:10 +0000 Subject: [PATCH 08/11] Optimize --- src/utils/DynamicArrayLib.sol | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index 83d898e11..fde7fcf3d 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -151,13 +151,12 @@ library DynamicArrayLib { 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. - mstore(end, needle) - for {} 1 {} { + for { mstore(end, needle) } 1 {} { o := add(o, 0x20) if eq(mload(o), needle) { break } } mstore(end, c) // Restore the word after the array. - result := or(sub(0, eq(o, end)), shr(5, sub(o, add(0x20, array)))) + if iszero(eq(o, end)) { result := shr(5, sub(o, add(0x20, array))) } } } } @@ -183,16 +182,15 @@ library DynamicArrayLib { assembly { result := not(0) let n := mload(array) - if gt(from, n) { from := n } + if iszero(lt(from, n)) { from := n } if from { let o := add(shl(5, add(1, from)), array) - mstore(array, needle) - for {} 1 {} { + for { mstore(array, needle) } 1 {} { o := sub(o, 0x20) if eq(mload(o), needle) { break } } mstore(array, n) // Restore the length of the array. - result := or(sub(0, eq(o, array)), shr(5, sub(o, add(0x20, array)))) + if iszero(eq(o, array)) { result := shr(5, sub(o, add(0x20, array))) } } } } From a17f64134af200c76198c9ab9fb0699044578c62 Mon Sep 17 00:00:00 2001 From: Vectorized Date: Mon, 30 Sep 2024 18:34:18 +0000 Subject: [PATCH 09/11] Optimize --- src/utils/DynamicArrayLib.sol | 8 ++++---- test/DynamicArrayLib.t.sol | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index fde7fcf3d..c13f8ff43 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -134,7 +134,7 @@ library DynamicArrayLib { /// @dev Returns if `needle` is in `array`. function contains(uint256[] memory array, uint256 needle) internal pure returns (bool) { - return indexOf(array, needle, 0) != NOT_FOUND; + return ~indexOf(array, needle, 0) != 0; } /// @dev Returns the first index of `needle`, scanning forward from `from`. @@ -696,17 +696,17 @@ library DynamicArrayLib { /// @dev Returns if `needle` is in `array`. function contains(DynamicArray memory array, uint256 needle) internal pure returns (bool) { - return indexOf(array.data, needle, 0) != NOT_FOUND; + 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) != NOT_FOUND; + 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) != NOT_FOUND; + return ~indexOf(array.data, uint256(needle), 0) != 0; } /// @dev Returns the first index of `needle`, scanning forward from `from`. diff --git a/test/DynamicArrayLib.t.sol b/test/DynamicArrayLib.t.sol index 33da3ed91..815f5bc29 100644 --- a/test/DynamicArrayLib.t.sol +++ b/test/DynamicArrayLib.t.sol @@ -214,6 +214,36 @@ contract DynamicArrayLibTest is SoladyTest { } } + function testUint256Contains() public { + uint256 n = 50; + uint256[] memory a; + assertEq(DynamicArrayLib.contains(a, 0), false); + assertEq(DynamicArrayLib.contains(a, 1), false); + assertEq(DynamicArrayLib.contains(a, 2), false); + a = new uint256[](0); + assertEq(DynamicArrayLib.contains(a, 0), false); + assertEq(DynamicArrayLib.contains(a, 1), false); + assertEq(DynamicArrayLib.contains(a, 2), false); + a = new uint256[](1); + assertEq(DynamicArrayLib.contains(a, 0), true); + assertEq(DynamicArrayLib.contains(a, 1), false); + assertEq(DynamicArrayLib.contains(a, 2), false); + a = DynamicArrayLib.malloc(n); + unchecked { + for (uint256 i; i != n; ++i) { + a.set(i, i); + } + } + assertEq(DynamicArrayLib.contains(a, 0), true); + assertEq(DynamicArrayLib.contains(a, 1), true); + assertEq(DynamicArrayLib.contains(a, 10), true); + assertEq(DynamicArrayLib.contains(a, 31), true); + assertEq(DynamicArrayLib.contains(a, 32), true); + assertEq(DynamicArrayLib.contains(a, 49), true); + assertEq(DynamicArrayLib.contains(a, 50), false); + assertEq(DynamicArrayLib.contains(a, 100), false); + } + function testUint256ArrayIndexOf() public { uint256 n = 50; uint256[] memory a; From 82c7184f20e0d24f47371bd52ccfeebd8823c85b Mon Sep 17 00:00:00 2001 From: atarpara Date: Tue, 1 Oct 2024 10:26:29 +0530 Subject: [PATCH 10/11] =?UTF-8?q?=F0=9F=90=9E=20Fix=20lastIndexOf()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/DynamicArrayLib.sol | 6 +++--- test/DynamicArrayLib.t.sol | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/utils/DynamicArrayLib.sol b/src/utils/DynamicArrayLib.sol index c13f8ff43..2465e3846 100644 --- a/src/utils/DynamicArrayLib.sol +++ b/src/utils/DynamicArrayLib.sol @@ -182,9 +182,9 @@ library DynamicArrayLib { assembly { result := not(0) let n := mload(array) - if iszero(lt(from, n)) { from := n } - if from { - let o := add(shl(5, add(1, from)), 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 } diff --git a/test/DynamicArrayLib.t.sol b/test/DynamicArrayLib.t.sol index 815f5bc29..c5ced0dc2 100644 --- a/test/DynamicArrayLib.t.sol +++ b/test/DynamicArrayLib.t.sol @@ -342,6 +342,17 @@ contract DynamicArrayLibTest is SoladyTest { assertEq(DynamicArrayLib.lastIndexOf(a, 49), 49); assertEq(DynamicArrayLib.lastIndexOf(a, 50), DynamicArrayLib.NOT_FOUND); assertEq(DynamicArrayLib.lastIndexOf(a, 100), DynamicArrayLib.NOT_FOUND); + + // edge case + assertEq(DynamicArrayLib.lastIndexOf(a, 0, 0), 0); + assertEq(DynamicArrayLib.lastIndexOf(a, 1, 1), 1); + assertEq(DynamicArrayLib.lastIndexOf(a, 10, 10), 10); + assertEq(DynamicArrayLib.lastIndexOf(a, 31, 31), 31); + assertEq(DynamicArrayLib.lastIndexOf(a, 32, 32), 32); + assertEq(DynamicArrayLib.lastIndexOf(a, 49, 49), 49); + assertEq(DynamicArrayLib.lastIndexOf(a, 50, 50), DynamicArrayLib.NOT_FOUND); + assertEq(DynamicArrayLib.lastIndexOf(a, 100, 100), DynamicArrayLib.NOT_FOUND); + } function testUint256ArrayLastIndexOfDifferential( @@ -377,10 +388,12 @@ contract DynamicArrayLibTest is SoladyTest { { unchecked { uint256 n = array.length; - if (from > n) from = n; - for (uint256 i = from; i != 0;) { - --i; - if (array[i] == needle) return i; + if (n > 0) { + if (from >= n) from = (n - 1); + for (uint256 i = (from + 1); i != 0;) { + --i; + if (array[i] == needle) return i; + } } } return type(uint256).max; From 7d0f214c16b8a4f23806ee2128bbd23967d5b688 Mon Sep 17 00:00:00 2001 From: atarpara Date: Tue, 1 Oct 2024 10:33:48 +0530 Subject: [PATCH 11/11] Forge Fmt --- test/DynamicArrayLib.t.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/DynamicArrayLib.t.sol b/test/DynamicArrayLib.t.sol index c5ced0dc2..ae87a9d98 100644 --- a/test/DynamicArrayLib.t.sol +++ b/test/DynamicArrayLib.t.sol @@ -352,7 +352,6 @@ contract DynamicArrayLibTest is SoladyTest { assertEq(DynamicArrayLib.lastIndexOf(a, 49, 49), 49); assertEq(DynamicArrayLib.lastIndexOf(a, 50, 50), DynamicArrayLib.NOT_FOUND); assertEq(DynamicArrayLib.lastIndexOf(a, 100, 100), DynamicArrayLib.NOT_FOUND); - } function testUint256ArrayLastIndexOfDifferential(