// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.6.12; /// @notice An efficient implementation of a singly linked list of addresses /// @dev A mapping(address => address) tracks the 'next' pointer. A special address called the SENTINEL is used to denote the beginning and end of the list. library MappedSinglyLinkedList { /// @notice The special value address used to denote the end of the list address public constant SENTINEL = address(0x1); /// @notice The data structure to use for the list. struct Mapping { uint256 count; mapping(address => address) addressMap; } /// @notice Initializes the list. /// @dev It is important that this is called so that the SENTINEL is correctly setup. function initialize(Mapping storage self) internal { require(self.count == 0, "Already init"); self.addressMap[SENTINEL] = SENTINEL; } function start(Mapping storage self) internal view returns (address) { return self.addressMap[SENTINEL]; } function next(Mapping storage self, address current) internal view returns (address) { return self.addressMap[current]; } function end(Mapping storage) internal pure returns (address) { return SENTINEL; } function addAddresses(Mapping storage self, address[] memory addresses) internal { for (uint256 i = 0; i < addresses.length; i++) { addAddress(self, addresses[i]); } } /// @notice Adds an address to the front of the list. /// @param self The Mapping struct that this function is attached to /// @param newAddress The address to shift to the front of the list function addAddress(Mapping storage self, address newAddress) internal { require(newAddress != SENTINEL && newAddress != address(0), "Invalid address"); require(self.addressMap[newAddress] == address(0), "Already added"); self.addressMap[newAddress] = self.addressMap[SENTINEL]; self.addressMap[SENTINEL] = newAddress; self.count = self.count + 1; } /// @notice Removes an address from the list /// @param self The Mapping struct that this function is attached to /// @param prevAddress The address that precedes the address to be removed. This may be the SENTINEL if at the start. /// @param addr The address to remove from the list. function removeAddress(Mapping storage self, address prevAddress, address addr) internal { require(addr != SENTINEL && addr != address(0), "Invalid address"); require(self.addressMap[prevAddress] == addr, "Invalid prevAddress"); self.addressMap[prevAddress] = self.addressMap[addr]; delete self.addressMap[addr]; self.count = self.count - 1; } /// @notice Determines whether the list contains the given address /// @param self The Mapping struct that this function is attached to /// @param addr The address to check /// @return True if the address is contained, false otherwise. function contains(Mapping storage self, address addr) internal view returns (bool) { return addr != SENTINEL && addr != address(0) && self.addressMap[addr] != address(0); } /// @notice Returns an address array of all the addresses in this list /// @dev Contains a for loop, so complexity is O(n) wrt the list size /// @param self The Mapping struct that this function is attached to /// @return An array of all the addresses function addressArray(Mapping storage self) internal view returns (address[] memory) { address[] memory array = new address[](self.count); uint256 count; address currentAddress = self.addressMap[SENTINEL]; while (currentAddress != address(0) && currentAddress != SENTINEL) { array[count] = currentAddress; currentAddress = self.addressMap[currentAddress]; count++; } return array; } /// @notice Removes every address from the list /// @param self The Mapping struct that this function is attached to function clearAll(Mapping storage self) internal { address currentAddress = self.addressMap[SENTINEL]; while (currentAddress != address(0) && currentAddress != SENTINEL) { address nextAddress = self.addressMap[currentAddress]; delete self.addressMap[currentAddress]; currentAddress = nextAddress; } self.addressMap[SENTINEL] = SENTINEL; self.count = 0; } }