You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.9 KiB
167 lines
4.9 KiB
4 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
pragma solidity >=0.6.11;
|
||
|
|
||
|
/**
|
||
|
* @title QueueLib
|
||
|
* @author Celo Labs Inc.
|
||
|
* @notice Library containing queue struct and operations for queue used by
|
||
|
* Home and Replica.
|
||
|
**/
|
||
|
library QueueLib {
|
||
|
/**
|
||
|
* @notice Queue struct
|
||
|
* @dev Internally keeps track of the `first` and `last` elements through
|
||
|
* indices and a mapping of indices to enqueued elements.
|
||
|
**/
|
||
|
struct Queue {
|
||
|
uint128 first;
|
||
|
uint128 last;
|
||
|
mapping(uint256 => bytes32) queue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Initializes the queue
|
||
|
* @dev Empty state denoted by _q.first > q._last. Queue initialized
|
||
|
* with _q.first = 1 and _q.last = 0.
|
||
|
**/
|
||
|
function initialize(Queue storage _q) internal {
|
||
|
if (_q.first == 0) {
|
||
|
_q.first = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Enqueues a single new element
|
||
|
* @param _item New element to be enqueued
|
||
|
* @return _last Index of newly enqueued element
|
||
|
**/
|
||
|
function enqueue(Queue storage _q, bytes32 _item)
|
||
|
internal
|
||
|
returns (uint128 _last)
|
||
|
{
|
||
|
_last = _q.last + 1;
|
||
|
_q.last = _last;
|
||
|
if (_item != bytes32(0)) {
|
||
|
// saves gas if we're queueing 0
|
||
|
_q.queue[_last] = _item;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Dequeues element at front of queue
|
||
|
* @dev Removes dequeued element from storage
|
||
|
* @return _item Dequeued element
|
||
|
**/
|
||
|
function dequeue(Queue storage _q) internal returns (bytes32 _item) {
|
||
|
uint128 _last = _q.last;
|
||
|
uint128 _first = _q.first;
|
||
|
require(_length(_last, _first) != 0, "Empty");
|
||
|
_item = _q.queue[_first];
|
||
|
if (_item != bytes32(0)) {
|
||
|
// saves gas if we're dequeuing 0
|
||
|
delete _q.queue[_first];
|
||
|
}
|
||
|
_q.first = _first + 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Batch enqueues several elements
|
||
|
* @param _items Array of elements to be enqueued
|
||
|
* @return _last Index of last enqueued element
|
||
|
**/
|
||
|
function enqueue(Queue storage _q, bytes32[] memory _items)
|
||
|
internal
|
||
|
returns (uint128 _last)
|
||
|
{
|
||
|
_last = _q.last;
|
||
|
for (uint256 i = 0; i < _items.length; i += 1) {
|
||
|
_last += 1;
|
||
|
bytes32 _item = _items[i];
|
||
|
if (_item != bytes32(0)) {
|
||
|
_q.queue[_last] = _item;
|
||
|
}
|
||
|
}
|
||
|
_q.last = _last;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Batch dequeues `_number` elements
|
||
|
* @dev Reverts if `_number` > queue length
|
||
|
* @param _number Number of elements to dequeue
|
||
|
* @return Array of dequeued elements
|
||
|
**/
|
||
|
function dequeue(Queue storage _q, uint256 _number)
|
||
|
internal
|
||
|
returns (bytes32[] memory)
|
||
|
{
|
||
|
uint128 _last = _q.last;
|
||
|
uint128 _first = _q.first;
|
||
|
// Cannot underflow unless state is corrupted
|
||
|
require(_length(_last, _first) >= _number, "Insufficient");
|
||
|
|
||
|
bytes32[] memory _items = new bytes32[](_number);
|
||
|
|
||
|
for (uint256 i = 0; i < _number; i++) {
|
||
|
_items[i] = _q.queue[_first];
|
||
|
delete _q.queue[_first];
|
||
|
_first++;
|
||
|
}
|
||
|
_q.first = _first;
|
||
|
return _items;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Returns true if `_item` is in the queue and false if otherwise
|
||
|
* @dev Linearly scans from _q.first to _q.last looking for `_item`
|
||
|
* @param _item Item being searched for in queue
|
||
|
* @return True if `_item` currently exists in queue, false if otherwise
|
||
|
**/
|
||
|
function contains(Queue storage _q, bytes32 _item)
|
||
|
internal
|
||
|
view
|
||
|
returns (bool)
|
||
|
{
|
||
|
for (uint256 i = _q.first; i <= _q.last; i++) {
|
||
|
if (_q.queue[i] == _item) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// @notice Returns last item in queue
|
||
|
/// @dev Returns bytes32(0) if queue empty
|
||
|
function lastItem(Queue storage _q) internal view returns (bytes32) {
|
||
|
return _q.queue[_q.last];
|
||
|
}
|
||
|
|
||
|
/// @notice Returns element at front of queue without removing element
|
||
|
/// @dev Reverts if queue is empty
|
||
|
function peek(Queue storage _q) internal view returns (bytes32 _item) {
|
||
|
require(!isEmpty(_q), "Empty");
|
||
|
_item = _q.queue[_q.first];
|
||
|
}
|
||
|
|
||
|
/// @notice Returns true if queue is empty and false if otherwise
|
||
|
function isEmpty(Queue storage _q) internal view returns (bool) {
|
||
|
return _q.last < _q.first;
|
||
|
}
|
||
|
|
||
4 years ago
|
/// @notice Returns number of elements in queue
|
||
|
function length(Queue storage _q) internal view returns (uint256) {
|
||
|
uint128 _last = _q.last;
|
||
|
uint128 _first = _q.first;
|
||
|
// Cannot underflow unless state is corrupted
|
||
|
return _length(_last, _first);
|
||
|
}
|
||
|
|
||
4 years ago
|
/// @notice Returns number of elements between `_last` and `_first` (used internally)
|
||
|
function _length(uint128 _last, uint128 _first)
|
||
|
internal
|
||
|
pure
|
||
|
returns (uint256)
|
||
|
{
|
||
|
return uint256(_last + 1 - _first);
|
||
|
}
|
||
|
}
|