DoubleEndedQueue.sol 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/DoubleEndedQueue.sol)
  3. pragma solidity ^0.8.20;
  4. import {Panic} from "../Panic.sol";
  5. /**
  6. * @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
  7. * the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
  8. * FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
  9. * the existing queue contents are left in storage.
  10. *
  11. * The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be
  12. * used in storage, and not in memory.
  13. * ```solidity
  14. * DoubleEndedQueue.Bytes32Deque queue;
  15. * ```
  16. */
  17. library DoubleEndedQueue {
  18. /**
  19. * @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
  20. *
  21. * Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
  22. * directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
  23. * lead to unexpected behavior.
  24. *
  25. * The first item is at data[begin] and the last item is at data[end - 1]. This range can wrap around.
  26. */
  27. struct Bytes32Deque {
  28. uint128 _begin;
  29. uint128 _end;
  30. mapping(uint128 index => bytes32) _data;
  31. }
  32. /**
  33. * @dev Inserts an item at the end of the queue.
  34. *
  35. * Reverts with {Panic-RESOURCE_ERROR} if the queue is full.
  36. */
  37. function pushBack(Bytes32Deque storage deque, bytes32 value) internal {
  38. unchecked {
  39. uint128 backIndex = deque._end;
  40. if (backIndex + 1 == deque._begin) Panic.panic(Panic.RESOURCE_ERROR);
  41. deque._data[backIndex] = value;
  42. deque._end = backIndex + 1;
  43. }
  44. }
  45. /**
  46. * @dev Removes the item at the end of the queue and returns it.
  47. *
  48. * Reverts with {Panic-EMPTY_ARRAY_POP} if the queue is empty.
  49. */
  50. function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) {
  51. unchecked {
  52. uint128 backIndex = deque._end;
  53. if (backIndex == deque._begin) Panic.panic(Panic.EMPTY_ARRAY_POP);
  54. --backIndex;
  55. value = deque._data[backIndex];
  56. delete deque._data[backIndex];
  57. deque._end = backIndex;
  58. }
  59. }
  60. /**
  61. * @dev Inserts an item at the beginning of the queue.
  62. *
  63. * Reverts with {Panic-RESOURCE_ERROR} if the queue is full.
  64. */
  65. function pushFront(Bytes32Deque storage deque, bytes32 value) internal {
  66. unchecked {
  67. uint128 frontIndex = deque._begin - 1;
  68. if (frontIndex == deque._end) Panic.panic(Panic.RESOURCE_ERROR);
  69. deque._data[frontIndex] = value;
  70. deque._begin = frontIndex;
  71. }
  72. }
  73. /**
  74. * @dev Removes the item at the beginning of the queue and returns it.
  75. *
  76. * Reverts with {Panic-EMPTY_ARRAY_POP} if the queue is empty.
  77. */
  78. function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) {
  79. unchecked {
  80. uint128 frontIndex = deque._begin;
  81. if (frontIndex == deque._end) Panic.panic(Panic.EMPTY_ARRAY_POP);
  82. value = deque._data[frontIndex];
  83. delete deque._data[frontIndex];
  84. deque._begin = frontIndex + 1;
  85. }
  86. }
  87. /**
  88. * @dev Returns the item at the beginning of the queue.
  89. *
  90. * Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the queue is empty.
  91. */
  92. function front(Bytes32Deque storage deque) internal view returns (bytes32 value) {
  93. if (empty(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
  94. return deque._data[deque._begin];
  95. }
  96. /**
  97. * @dev Returns the item at the end of the queue.
  98. *
  99. * Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the queue is empty.
  100. */
  101. function back(Bytes32Deque storage deque) internal view returns (bytes32 value) {
  102. if (empty(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
  103. unchecked {
  104. return deque._data[deque._end - 1];
  105. }
  106. }
  107. /**
  108. * @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
  109. * `length(deque) - 1`.
  110. *
  111. * Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the index is out of bounds.
  112. */
  113. function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) {
  114. if (index >= length(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
  115. // By construction, length is a uint128, so the check above ensures that index can be safely downcast to uint128
  116. unchecked {
  117. return deque._data[deque._begin + uint128(index)];
  118. }
  119. }
  120. /**
  121. * @dev Resets the queue back to being empty.
  122. *
  123. * NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
  124. * out on potential gas refunds.
  125. */
  126. function clear(Bytes32Deque storage deque) internal {
  127. deque._begin = 0;
  128. deque._end = 0;
  129. }
  130. /**
  131. * @dev Returns the number of items in the queue.
  132. */
  133. function length(Bytes32Deque storage deque) internal view returns (uint256) {
  134. unchecked {
  135. return uint256(deque._end - deque._begin);
  136. }
  137. }
  138. /**
  139. * @dev Returns true if the queue is empty.
  140. */
  141. function empty(Bytes32Deque storage deque) internal view returns (bool) {
  142. return deque._end == deque._begin;
  143. }
  144. }