Arrays.sol 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.0.0) (utils/Arrays.sol)
  3. pragma solidity ^0.8.20;
  4. import {StorageSlot} from "./StorageSlot.sol";
  5. import {Math} from "./math/Math.sol";
  6. /**
  7. * @dev Collection of functions related to array types.
  8. */
  9. library Arrays {
  10. using StorageSlot for bytes32;
  11. /**
  12. * @dev Sort an array (in memory) in increasing order.
  13. *
  14. * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
  15. * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
  16. *
  17. * NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
  18. * array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
  19. * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
  20. * consume more gas than is available in a block, leading to potential DoS.
  21. */
  22. function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
  23. _quickSort(array, 0, array.length);
  24. return array;
  25. }
  26. /**
  27. * @dev Performs a quick sort on an array in memory. The array is sorted in increasing order.
  28. *
  29. * Invariant: `i <= j <= array.length`. This is the case when initially called by {sort} and is preserved in
  30. * subcalls.
  31. */
  32. function _quickSort(uint256[] memory array, uint256 i, uint256 j) private pure {
  33. unchecked {
  34. // Can't overflow given `i <= j`
  35. if (j - i < 2) return;
  36. // Use first element as pivot
  37. uint256 pivot = unsafeMemoryAccess(array, i);
  38. // Position where the pivot should be at the end of the loop
  39. uint256 index = i;
  40. for (uint256 k = i + 1; k < j; ++k) {
  41. // Unsafe access is safe given `k < j <= array.length`.
  42. if (unsafeMemoryAccess(array, k) < pivot) {
  43. // If array[k] is smaller than the pivot, we increment the index and move array[k] there.
  44. _swap(array, ++index, k);
  45. }
  46. }
  47. // Swap pivot into place
  48. _swap(array, i, index);
  49. _quickSort(array, i, index); // Sort the left side of the pivot
  50. _quickSort(array, index + 1, j); // Sort the right side of the pivot
  51. }
  52. }
  53. /**
  54. * @dev Swaps the elements at positions `i` and `j` in the `arr` array.
  55. */
  56. function _swap(uint256[] memory arr, uint256 i, uint256 j) private pure {
  57. assembly {
  58. let start := add(arr, 0x20) // Pointer to the first element of the array
  59. let pos_i := add(start, mul(i, 0x20))
  60. let pos_j := add(start, mul(j, 0x20))
  61. let val_i := mload(pos_i)
  62. let val_j := mload(pos_j)
  63. mstore(pos_i, val_j)
  64. mstore(pos_j, val_i)
  65. }
  66. }
  67. /**
  68. * @dev Searches a sorted `array` and returns the first index that contains
  69. * a value greater or equal to `element`. If no such index exists (i.e. all
  70. * values in the array are strictly less than `element`), the array length is
  71. * returned. Time complexity O(log n).
  72. *
  73. * NOTE: The `array` is expected to be sorted in ascending order, and to
  74. * contain no repeated elements.
  75. *
  76. * IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
  77. * support for repeated elements in the array. The {lowerBound} function should
  78. * be used instead.
  79. */
  80. function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
  81. uint256 low = 0;
  82. uint256 high = array.length;
  83. if (high == 0) {
  84. return 0;
  85. }
  86. while (low < high) {
  87. uint256 mid = Math.average(low, high);
  88. // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
  89. // because Math.average rounds towards zero (it does integer division with truncation).
  90. if (unsafeAccess(array, mid).value > element) {
  91. high = mid;
  92. } else {
  93. low = mid + 1;
  94. }
  95. }
  96. // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
  97. if (low > 0 && unsafeAccess(array, low - 1).value == element) {
  98. return low - 1;
  99. } else {
  100. return low;
  101. }
  102. }
  103. /**
  104. * @dev Searches an `array` sorted in ascending order and returns the first
  105. * index that contains a value greater or equal than `element`. If no such index
  106. * exists (i.e. all values in the array are strictly less than `element`), the array
  107. * length is returned. Time complexity O(log n).
  108. *
  109. * See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
  110. */
  111. function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
  112. uint256 low = 0;
  113. uint256 high = array.length;
  114. if (high == 0) {
  115. return 0;
  116. }
  117. while (low < high) {
  118. uint256 mid = Math.average(low, high);
  119. // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
  120. // because Math.average rounds towards zero (it does integer division with truncation).
  121. if (unsafeAccess(array, mid).value < element) {
  122. // this cannot overflow because mid < high
  123. unchecked {
  124. low = mid + 1;
  125. }
  126. } else {
  127. high = mid;
  128. }
  129. }
  130. return low;
  131. }
  132. /**
  133. * @dev Searches an `array` sorted in ascending order and returns the first
  134. * index that contains a value strictly greater than `element`. If no such index
  135. * exists (i.e. all values in the array are strictly less than `element`), the array
  136. * length is returned. Time complexity O(log n).
  137. *
  138. * See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
  139. */
  140. function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
  141. uint256 low = 0;
  142. uint256 high = array.length;
  143. if (high == 0) {
  144. return 0;
  145. }
  146. while (low < high) {
  147. uint256 mid = Math.average(low, high);
  148. // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
  149. // because Math.average rounds towards zero (it does integer division with truncation).
  150. if (unsafeAccess(array, mid).value > element) {
  151. high = mid;
  152. } else {
  153. // this cannot overflow because mid < high
  154. unchecked {
  155. low = mid + 1;
  156. }
  157. }
  158. }
  159. return low;
  160. }
  161. /**
  162. * @dev Same as {lowerBound}, but with an array in memory.
  163. */
  164. function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
  165. uint256 low = 0;
  166. uint256 high = array.length;
  167. if (high == 0) {
  168. return 0;
  169. }
  170. while (low < high) {
  171. uint256 mid = Math.average(low, high);
  172. // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
  173. // because Math.average rounds towards zero (it does integer division with truncation).
  174. if (unsafeMemoryAccess(array, mid) < element) {
  175. // this cannot overflow because mid < high
  176. unchecked {
  177. low = mid + 1;
  178. }
  179. } else {
  180. high = mid;
  181. }
  182. }
  183. return low;
  184. }
  185. /**
  186. * @dev Same as {upperBound}, but with an array in memory.
  187. */
  188. function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
  189. uint256 low = 0;
  190. uint256 high = array.length;
  191. if (high == 0) {
  192. return 0;
  193. }
  194. while (low < high) {
  195. uint256 mid = Math.average(low, high);
  196. // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
  197. // because Math.average rounds towards zero (it does integer division with truncation).
  198. if (unsafeMemoryAccess(array, mid) > element) {
  199. high = mid;
  200. } else {
  201. // this cannot overflow because mid < high
  202. unchecked {
  203. low = mid + 1;
  204. }
  205. }
  206. }
  207. return low;
  208. }
  209. /**
  210. * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  211. *
  212. * WARNING: Only use if you are certain `pos` is lower than the array length.
  213. */
  214. function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
  215. bytes32 slot;
  216. // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
  217. // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
  218. /// @solidity memory-safe-assembly
  219. assembly {
  220. mstore(0, arr.slot)
  221. slot := add(keccak256(0, 0x20), pos)
  222. }
  223. return slot.getAddressSlot();
  224. }
  225. /**
  226. * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  227. *
  228. * WARNING: Only use if you are certain `pos` is lower than the array length.
  229. */
  230. function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
  231. bytes32 slot;
  232. // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
  233. // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
  234. /// @solidity memory-safe-assembly
  235. assembly {
  236. mstore(0, arr.slot)
  237. slot := add(keccak256(0, 0x20), pos)
  238. }
  239. return slot.getBytes32Slot();
  240. }
  241. /**
  242. * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  243. *
  244. * WARNING: Only use if you are certain `pos` is lower than the array length.
  245. */
  246. function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
  247. bytes32 slot;
  248. // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
  249. // following https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
  250. /// @solidity memory-safe-assembly
  251. assembly {
  252. mstore(0, arr.slot)
  253. slot := add(keccak256(0, 0x20), pos)
  254. }
  255. return slot.getUint256Slot();
  256. }
  257. /**
  258. * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  259. *
  260. * WARNING: Only use if you are certain `pos` is lower than the array length.
  261. */
  262. function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
  263. assembly {
  264. res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
  265. }
  266. }
  267. /**
  268. * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  269. *
  270. * WARNING: Only use if you are certain `pos` is lower than the array length.
  271. */
  272. function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
  273. assembly {
  274. res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
  275. }
  276. }
  277. /**
  278. * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  279. *
  280. * WARNING: Only use if you are certain `pos` is lower than the array length.
  281. */
  282. function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
  283. assembly {
  284. res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
  285. }
  286. }
  287. }