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

SortedLinkedList Foundry Migration #10846

Merged
merged 19 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
pragma solidity >=0.5.13 <0.8.20;

import "@openzeppelin/contracts8/utils/math/SafeMath.sol";

import "./SortedLinkedList.sol";

/**
* @title Maintains a sorted list of unsigned ints keyed by uint256.
*/
library IntegerSortedLinkedList {
using SafeMath for uint256;
using SortedLinkedList for SortedLinkedList.List;

/**
* @notice Inserts an element into a doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to insert.
* @param value The element value.
* @param lesserKey The key of the element less than the element to insert.
* @param greaterKey The key of the element greater than the element to insert.
*/
function insert(
SortedLinkedList.List storage list,
uint256 key,
uint256 value,
uint256 lesserKey,
uint256 greaterKey
) public {
list.insert(bytes32(key), value, bytes32(lesserKey), bytes32(greaterKey));
}

/**
* @notice Removes an element from the doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to remove.
*/
function remove(SortedLinkedList.List storage list, uint256 key) public {
list.remove(bytes32(key));
}

/**
* @notice Updates an element in the list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @param value The element value.
* @param lesserKey The key of the element will be just left of `key` after the update.
* @param greaterKey The key of the element will be just right of `key` after the update.
* @dev Note that only one of "lesserKey" or "greaterKey" needs to be correct to reduce friction.
*/
function update(
SortedLinkedList.List storage list,
uint256 key,
uint256 value,
uint256 lesserKey,
uint256 greaterKey
) public {
list.update(bytes32(key), value, bytes32(lesserKey), bytes32(greaterKey));
}

/**
* @notice Inserts an element at the end of the doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to insert.
*/
function push(SortedLinkedList.List storage list, uint256 key) public {
list.push(bytes32(key));
}

/**
* @notice Removes N elements from the head of the list and returns their keys.
* @param list A storage pointer to the underlying list.
* @param n The number of elements to pop.
* @return The keys of the popped elements.
*/
function popN(SortedLinkedList.List storage list, uint256 n) public returns (uint256[] memory) {
bytes32[] memory byteKeys = list.popN(n);
uint256[] memory keys = new uint256[](byteKeys.length);
for (uint256 i = 0; i < byteKeys.length; i = i.add(1)) {
keys[i] = uint256(byteKeys[i]);
}
return keys;
}

/**
* @notice Returns whether or not a particular key is present in the sorted list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @return Whether or not the key is in the sorted list.
*/
function contains(SortedLinkedList.List storage list, uint256 key) public view returns (bool) {
return list.contains(bytes32(key));
}

/**
* @notice Returns the value for a particular key in the sorted list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @return The element value.
*/
function getValue(SortedLinkedList.List storage list, uint256 key) public view returns (uint256) {
return list.getValue(bytes32(key));
}

/**
* @notice Gets all elements from the doubly linked list.
* @param list A storage pointer to the underlying list.
* @return Array of all keys in the list.
* @return Values corresponding to keys, which will be ordered largest to smallest.
*/
function getElements(SortedLinkedList.List storage list)
public
view
returns (uint256[] memory, uint256[] memory)
{
bytes32[] memory byteKeys = list.getKeys();
uint256[] memory keys = new uint256[](byteKeys.length);
uint256[] memory values = new uint256[](byteKeys.length);
for (uint256 i = 0; i < byteKeys.length; i = i.add(1)) {
keys[i] = uint256(byteKeys[i]);
values[i] = list.values[byteKeys[i]];
}
return (keys, values);
}
}
167 changes: 167 additions & 0 deletions packages/protocol/contracts-0.8/common/linkedlists/LinkedList.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
pragma solidity >=0.5.13 <0.8.20;
soloseng marked this conversation as resolved.
Show resolved Hide resolved

import "@openzeppelin/contracts8/utils/math/SafeMath.sol";

/**
* @title Maintains a doubly linked list keyed by bytes32.
* @dev Following the `next` pointers will lead you to the head, rather than the tail.
*/
library LinkedList {
using SafeMath for uint256;

struct Element {
bytes32 previousKey;
bytes32 nextKey;
bool exists;
}

struct List {
bytes32 head;
bytes32 tail;
uint256 numElements;
mapping(bytes32 => Element) elements;
}

/**
* @notice Inserts an element into a doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to insert.
* @param previousKey The key of the element that comes before the element to insert.
* @param nextKey The key of the element that comes after the element to insert.
*/
function insert(List storage list, bytes32 key, bytes32 previousKey, bytes32 nextKey) internal {
require(key != bytes32(0), "Key must be defined");
require(!contains(list, key), "Can't insert an existing element");
require(
previousKey != key && nextKey != key,
"Key cannot be the same as previousKey or nextKey"
);

Element storage element = list.elements[key];
element.exists = true;

if (list.numElements == 0) {
list.tail = key;
list.head = key;
} else {
require(
previousKey != bytes32(0) || nextKey != bytes32(0),
"Either previousKey or nextKey must be defined"
);

element.previousKey = previousKey;
element.nextKey = nextKey;

if (previousKey != bytes32(0)) {
require(
contains(list, previousKey),
"If previousKey is defined, it must exist in the list"
);
Element storage previousElement = list.elements[previousKey];
require(previousElement.nextKey == nextKey, "previousKey must be adjacent to nextKey");
previousElement.nextKey = key;
} else {
list.tail = key;
}

if (nextKey != bytes32(0)) {
require(contains(list, nextKey), "If nextKey is defined, it must exist in the list");
Element storage nextElement = list.elements[nextKey];
require(nextElement.previousKey == previousKey, "previousKey must be adjacent to nextKey");
nextElement.previousKey = key;
} else {
list.head = key;
}
}

list.numElements = list.numElements.add(1);
}

/**
* @notice Inserts an element at the tail of the doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to insert.
*/
function push(List storage list, bytes32 key) internal {
insert(list, key, bytes32(0), list.tail);
}

/**
* @notice Removes an element from the doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to remove.
*/
function remove(List storage list, bytes32 key) internal {
Element storage element = list.elements[key];
require(key != bytes32(0) && contains(list, key), "key not in list");
if (element.previousKey != bytes32(0)) {
Element storage previousElement = list.elements[element.previousKey];
previousElement.nextKey = element.nextKey;
} else {
list.tail = element.nextKey;
}

if (element.nextKey != bytes32(0)) {
Element storage nextElement = list.elements[element.nextKey];
nextElement.previousKey = element.previousKey;
} else {
list.head = element.previousKey;
}

delete list.elements[key];
list.numElements = list.numElements.sub(1);
}

/**
* @notice Updates an element in the list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @param previousKey The key of the element that comes before the updated element.
* @param nextKey The key of the element that comes after the updated element.
*/
function update(List storage list, bytes32 key, bytes32 previousKey, bytes32 nextKey) internal {
require(
key != bytes32(0) && key != previousKey && key != nextKey && contains(list, key),
"key on in list"
);
remove(list, key);
insert(list, key, previousKey, nextKey);
}

/**
* @notice Returns whether or not a particular key is present in the sorted list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @return Whether or not the key is in the sorted list.
*/
function contains(List storage list, bytes32 key) internal view returns (bool) {
return list.elements[key].exists;
}

/**
* @notice Returns the keys of the N elements at the head of the list.
* @param list A storage pointer to the underlying list.
* @param n The number of elements to return.
* @return The keys of the N elements at the head of the list.
* @dev Reverts if n is greater than the number of elements in the list.
*/
function headN(List storage list, uint256 n) internal view returns (bytes32[] memory) {
require(n <= list.numElements, "not enough elements");
bytes32[] memory keys = new bytes32[](n);
bytes32 key = list.head;
for (uint256 i = 0; i < n; i = i.add(1)) {
keys[i] = key;
key = list.elements[key].previousKey;
}
return keys;
}

/**
* @notice Gets all element keys from the doubly linked list.
* @param list A storage pointer to the underlying list.
* @return All element keys from head to tail.
*/
function getKeys(List storage list) internal view returns (bytes32[] memory) {
return headN(list, list.numElements);
}
}
Loading
Loading