-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
129 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.13; | ||
|
||
import {UintVec, VecLib} from "./Vec.sol"; | ||
|
||
struct UsedIndex { | ||
uint256 fromIndex; | ||
uint256 toIndex; | ||
} | ||
|
||
struct UsedIndexMap { | ||
uint256 length; | ||
UintVec usedIndicesPtrs; | ||
} | ||
|
||
using UsedIndexMapLib for UsedIndexMap global; | ||
|
||
/// @author philogy <https://github.com/philogy> | ||
library UsedIndexMapLib { | ||
function init(UsedIndexMap memory self, uint256 length, uint256 startCapacity) internal pure { | ||
self.length = length; | ||
self.usedIndicesPtrs = VecLib.uint_with_cap(startCapacity); | ||
} | ||
|
||
function lookupIndex(UsedIndexMap memory self, uint256 index) | ||
internal | ||
pure | ||
returns (bool isUsed, uint256 usedIndex, uint256 realIndex) | ||
{ | ||
require(index < self.length, "Index out-of-bounds"); | ||
|
||
function(UintVec memory, uint256) pure returns (uint256) vecUintGet = VecLib.get; | ||
function(UintVec memory, uint256) pure returns (UsedIndex memory) vecUsedIndexGet; | ||
assembly { | ||
vecUsedIndexGet := vecUintGet | ||
} | ||
|
||
for (usedIndex = 0; usedIndex < self.usedIndicesPtrs.length; usedIndex++) { | ||
UsedIndex memory used = vecUsedIndexGet(self.usedIndicesPtrs, usedIndex); | ||
if (used.fromIndex == index) { | ||
isUsed = true; | ||
realIndex = used.toIndex; | ||
return (isUsed, usedIndex, realIndex); | ||
} | ||
} | ||
|
||
return (false, usedIndex, index); | ||
} | ||
|
||
function mapIndex(UsedIndexMap memory self, uint256 index) internal pure returns (uint256 realIndex) { | ||
(,, realIndex) = self.lookupIndex(index); | ||
} | ||
|
||
function useIndex(UsedIndexMap memory self, uint256 index) internal pure returns (uint256 realIndex) { | ||
require(self.length > 0, "Nothing to use"); | ||
bool isUsed; | ||
uint256 usedIndex; | ||
(isUsed, usedIndex, realIndex) = self.lookupIndex(index); | ||
(,, uint256 lastIndex) = self.lookupIndex(self.length - 1); | ||
self.length -= 1; | ||
if (isUsed) { | ||
function(UsedIndex memory, uint256) pure setUsed = _set; | ||
function(uint256, uint256) pure setWithPtr; | ||
assembly { | ||
setWithPtr := setUsed | ||
} | ||
setWithPtr(self.usedIndicesPtrs.get(usedIndex), lastIndex); | ||
} else { | ||
UsedIndex memory newUsed = UsedIndex({fromIndex: index, toIndex: lastIndex}); | ||
uint256 ptr; | ||
assembly { | ||
ptr := newUsed | ||
} | ||
self.usedIndicesPtrs.push(ptr); | ||
} | ||
} | ||
|
||
function _set(UsedIndex memory used, uint256 newToIndex) private pure { | ||
used.toIndex = newToIndex; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {UsedIndexMap} from "src/collections/UsedIndexMap.sol"; | ||
import {PRNG} from "src/collections/PRNG.sol"; | ||
|
||
import {console} from "forge-std/console.sol"; | ||
import {FormatLib} from "src/libraries/FormatLib.sol"; | ||
|
||
/// @author philogy <https://github.com/philogy> | ||
contract UsedIndexMapTest is Test { | ||
using FormatLib for *; | ||
|
||
function setUp() public {} | ||
|
||
uint256[] ogNums; | ||
uint256[] nums; | ||
|
||
function test_fuzzing_randomUse(uint256 length, PRNG memory rng) public { | ||
length = bound(length, 1, 1000); | ||
for (uint256 i = 0; i < length; i++) { | ||
ogNums.push(i); | ||
nums.push(i); | ||
} | ||
UsedIndexMap memory map; | ||
map.init(length, length / 2); | ||
|
||
uint256 iters = rng.randuint(1, length + 1); | ||
for (uint256 i = 0; i < iters; i++) { | ||
uint256 index = rng.randuint(nums.length); | ||
|
||
uint256 num = nums[index]; | ||
uint256 ui = map.useIndex(index); | ||
uint256 mappedNum = ogNums[ui]; | ||
|
||
assertEq(num, mappedNum); | ||
|
||
uint256 lastIndex = nums.length - 1; | ||
uint256 lastNum = nums[lastIndex]; | ||
nums.pop(); | ||
|
||
if (index != lastIndex) { | ||
nums[index] = lastNum; | ||
} | ||
} | ||
} | ||
} |