Contract Name:
StructuredLinkedList
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title StructuredLinkedList
* @author Vittorio Minacori (https://github.com/vittominacori)
* @dev An utility library for using sorted linked list data structures in your Solidity project.
* @notice Adapted from
* https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/src/contracts/libraries/StructuredLinkedList.sol
*/
library StructuredLinkedList {
uint256 private constant _NULL = 0;
uint256 private constant _HEAD = 0;
bool private constant _PREV = false;
bool private constant _NEXT = true;
struct List {
uint256 size;
mapping(uint256 => mapping(bool => uint256)) list;
}
/**
* @dev Checks if the list exists
* @param self stored linked list from contract
* @return bool true if list exists, false otherwise
*/
function listExists(
List storage self
) public view returns (bool) {
// if the head nodes previous or next pointers both point to itself, then there are no items in the list
if (self.list[_HEAD][_PREV] != _HEAD || self.list[_HEAD][_NEXT] != _HEAD) {
return true;
} else {
return false;
}
}
/**
* @dev Checks if the node exists
* @param self stored linked list from contract
* @param _node a node to search for
* @return bool true if node exists, false otherwise
*/
function nodeExists(List storage self, uint256 _node) public view returns (bool) {
if (self.list[_node][_PREV] == _HEAD && self.list[_node][_NEXT] == _HEAD) {
if (self.list[_HEAD][_NEXT] == _node) {
return true;
} else {
return false;
}
} else {
return true;
}
}
/**
* @dev Returns the number of elements in the list
* @param self stored linked list from contract
* @return uint256
*/
// slither-disable-next-line dead-code
function sizeOf(
List storage self
) public view returns (uint256) {
return self.size;
}
/**
* @dev Gets the head of the list
* @param self stored linked list from contract
* @return uint256 the head of the list
*/
function getHead(
List storage self
) public view returns (uint256) {
return self.list[_HEAD][_NEXT];
}
/**
* @dev Gets the head of the list
* @param self stored linked list from contract
* @return uint256 the head of the list
*/
function getTail(
List storage self
) public view returns (uint256) {
return self.list[_HEAD][_PREV];
}
/**
* @dev Returns the links of a node as a tuple
* @param self stored linked list from contract
* @param _node id of the node to get
* @return bool, uint256, uint256 true if node exists or false otherwise, previous node, next node
*/
// slither-disable-next-line dead-code
function getNode(List storage self, uint256 _node) public view returns (bool, uint256, uint256) {
if (!nodeExists(self, _node)) {
return (false, 0, 0);
} else {
return (true, self.list[_node][_PREV], self.list[_node][_NEXT]);
}
}
/**
* @dev Returns the link of a node `_node` in direction `_direction`.
* @param self stored linked list from contract
* @param _node id of the node to step from
* @param _direction direction to step in
* @return bool, uint256 true if node exists or false otherwise, node in _direction
*/
// slither-disable-next-line dead-code
function getAdjacent(List storage self, uint256 _node, bool _direction) public view returns (bool, uint256) {
if (!nodeExists(self, _node)) {
return (false, 0);
} else {
uint256 adjacent = self.list[_node][_direction];
return (adjacent != _HEAD, adjacent);
}
}
/**
* @dev Returns the link of a node `_node` in direction `_NEXT`.
* @param self stored linked list from contract
* @param _node id of the node to step from
* @return bool, uint256 true if node exists or false otherwise, next node
*/
// slither-disable-next-line dead-code
function getNextNode(List storage self, uint256 _node) public view returns (bool, uint256) {
return getAdjacent(self, _node, _NEXT);
}
/**
* @dev Returns the link of a node `_node` in direction `_PREV`.
* @param self stored linked list from contract
* @param _node id of the node to step from
* @return bool, uint256 true if node exists or false otherwise, previous node
*/
// slither-disable-next-line dead-code
function getPreviousNode(List storage self, uint256 _node) public view returns (bool, uint256) {
return getAdjacent(self, _node, _PREV);
}
/**
* @dev Insert node `_new` beside existing node `_node` in direction `_NEXT`.
* @param self stored linked list from contract
* @param _node existing node
* @param _new new node to insert
* @return bool true if success, false otherwise
*/
// slither-disable-next-line dead-code
function insertAfter(List storage self, uint256 _node, uint256 _new) public returns (bool) {
return _insert(self, _node, _new, _NEXT);
}
/**
* @dev Insert node `_new` beside existing node `_node` in direction `_PREV`.
* @param self stored linked list from contract
* @param _node existing node
* @param _new new node to insert
* @return bool true if success, false otherwise
*/
// slither-disable-next-line dead-code
function insertBefore(List storage self, uint256 _node, uint256 _new) public returns (bool) {
return _insert(self, _node, _new, _PREV);
}
/**
* @dev Removes an entry from the linked list
* @param self stored linked list from contract
* @param _node node to remove from the list
* @return uint256 the removed node
*/
function remove(List storage self, uint256 _node) public returns (uint256) {
if ((_node == _NULL) || (!nodeExists(self, _node))) {
return 0;
}
_createLink(self, self.list[_node][_PREV], self.list[_node][_NEXT], _NEXT);
delete self.list[_node][_PREV];
delete self.list[_node][_NEXT];
self.size -= 1;
return _node;
}
/**
* @dev Pushes an entry to the head of the linked list
* @param self stored linked list from contract
* @param _node new entry to push to the head
* @return bool true if success, false otherwise
*/
function pushFront(List storage self, uint256 _node) public returns (bool) {
return _push(self, _node, _NEXT);
}
/**
* @dev Pushes an entry to the tail of the linked list
* @param self stored linked list from contract
* @param _node new entry to push to the tail
* @return bool true if success, false otherwise
*/
function pushBack(List storage self, uint256 _node) public returns (bool) {
return _push(self, _node, _PREV);
}
/**
* @dev Pops the first entry from the head of the linked list
* @param self stored linked list from contract
* @return uint256 the removed node
*/
// slither-disable-next-line dead-code
function popFront(
List storage self
) public returns (uint256) {
return _pop(self, _NEXT);
}
/**
* @dev Pops the first entry from the tail of the linked list
* @param self stored linked list from contract
* @return uint256 the removed node
*/
// slither-disable-next-line dead-code
function popBack(
List storage self
) public returns (uint256) {
return _pop(self, _PREV);
}
/**
* @dev Pushes an entry to the head of the linked list
* @param self stored linked list from contract
* @param _node new entry to push to the head
* @param _direction push to the head (_NEXT) or tail (_PREV)
* @return bool true if success, false otherwise
*/
function _push(List storage self, uint256 _node, bool _direction) private returns (bool) {
return _insert(self, _HEAD, _node, _direction);
}
/**
* @dev Pops the first entry from the linked list
* @param self stored linked list from contract
* @param _direction pop from the head (_NEXT) or the tail (_PREV)
* @return uint256 the removed node
*/
// slither-disable-next-line dead-code
function _pop(List storage self, bool _direction) private returns (uint256) {
uint256 adj;
(, adj) = getAdjacent(self, _HEAD, _direction);
return remove(self, adj);
}
/**
* @dev Insert node `_new` beside existing node `_node` in direction `_direction`.
* @param self stored linked list from contract
* @param _node existing node
* @param _new new node to insert
* @param _direction direction to insert node in
* @return bool true if success, false otherwise
*/
function _insert(List storage self, uint256 _node, uint256 _new, bool _direction) private returns (bool) {
if (!nodeExists(self, _new) && nodeExists(self, _node)) {
uint256 c = self.list[_node][_direction];
_createLink(self, _node, _new, _direction);
_createLink(self, _new, c, _direction);
self.size += 1;
return true;
}
return false;
}
/**
* @dev Creates a bidirectional link between two nodes on direction `_direction`
* @param self stored linked list from contract
* @param _node existing node
* @param _link node to link to in the _direction
* @param _direction direction to insert node in
*/
function _createLink(List storage self, uint256 _node, uint256 _link, bool _direction) private {
self.list[_link][!_direction] = _node;
self.list[_node][_direction] = _link;
}
}