Skip to content

Commit

Permalink
Extend Checkpoints with new sizes and lookup mechanisms (#3589)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx committed Aug 30, 2022
1 parent 4b16e88 commit 71aaca2
Show file tree
Hide file tree
Showing 20 changed files with 1,183 additions and 147 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -22,6 +22,9 @@
* `VestingWallet`: add `releasable` getters. ([#3580](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3580))
* `Create2`: optimize address computation by using assembly instead of `abi.encodePacked`. ([#3600](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3600))
* `Clones`: optimized the assembly to use only the scratch space during deployments, and optimized `predictDeterministicAddress` to use lesser operations. ([#3640](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3640))
* `Checkpoints`: Use procedural generation to support multiple key/value lengths. ([#3589](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3589))
* `Checkpoints`: Add new lookup mechanisms. ([#3589](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3589))
* `Array`: Add `unsafeAccess` functions that allow reading and writing to an element in a storage array bypassing Solidity's "out-of-bounds" check. ([#3589](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3589))

### Breaking changes

Expand Down
19 changes: 0 additions & 19 deletions contracts/mocks/ArraysImpl.sol

This file was deleted.

51 changes: 51 additions & 0 deletions contracts/mocks/ArraysMock.sol
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Arrays.sol";

contract Uint256ArraysMock {
using Arrays for uint256[];

uint256[] private _array;

constructor(uint256[] memory array) {
_array = array;
}

function findUpperBound(uint256 element) external view returns (uint256) {
return _array.findUpperBound(element);
}

function unsafeAccess(uint256 pos) external view returns (uint256) {
return _array.unsafeAccess(pos).value;
}
}

contract AddressArraysMock {
using Arrays for address[];

address[] private _array;

constructor(address[] memory array) {
_array = array;
}

function unsafeAccess(uint256 pos) external view returns (address) {
return _array.unsafeAccess(pos).value;
}
}

contract Bytes32ArraysMock {
using Arrays for bytes32[];

bytes32[] private _array;

constructor(bytes32[] memory array) {
_array = array;
}

function unsafeAccess(uint256 pos) external view returns (bytes32) {
return _array.unsafeAccess(pos).value;
}
}
27 changes: 0 additions & 27 deletions contracts/mocks/CheckpointsImpl.sol

This file was deleted.

92 changes: 92 additions & 0 deletions contracts/mocks/CheckpointsMock.sol
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/CheckpointsMock.js.

pragma solidity ^0.8.0;

import "../utils/Checkpoints.sol";

contract CheckpointsMock {
using Checkpoints for Checkpoints.History;

Checkpoints.History private _totalCheckpoints;

function latest() public view returns (uint256) {
return _totalCheckpoints.latest();
}

function push(uint256 value) public returns (uint256, uint256) {
return _totalCheckpoints.push(value);
}

function getAtBlock(uint256 blockNumber) public view returns (uint256) {
return _totalCheckpoints.getAtBlock(blockNumber);
}

function getAtRecentBlock(uint256 blockNumber) public view returns (uint256) {
return _totalCheckpoints.getAtRecentBlock(blockNumber);
}

function length() public view returns (uint256) {
return _totalCheckpoints._checkpoints.length;
}
}

contract Checkpoints224Mock {
using Checkpoints for Checkpoints.Trace224;

Checkpoints.Trace224 private _totalCheckpoints;

function latest() public view returns (uint224) {
return _totalCheckpoints.latest();
}

function push(uint32 key, uint224 value) public returns (uint224, uint224) {
return _totalCheckpoints.push(key, value);
}

function lowerLookup(uint32 key) public view returns (uint224) {
return _totalCheckpoints.lowerLookup(key);
}

function upperLookup(uint32 key) public view returns (uint224) {
return _totalCheckpoints.upperLookup(key);
}

function upperLookupRecent(uint32 key) public view returns (uint224) {
return _totalCheckpoints.upperLookupRecent(key);
}

function length() public view returns (uint256) {
return _totalCheckpoints._checkpoints.length;
}
}

contract Checkpoints160Mock {
using Checkpoints for Checkpoints.Trace160;

Checkpoints.Trace160 private _totalCheckpoints;

function latest() public view returns (uint160) {
return _totalCheckpoints.latest();
}

function push(uint96 key, uint160 value) public returns (uint160, uint160) {
return _totalCheckpoints.push(key, value);
}

function lowerLookup(uint96 key) public view returns (uint160) {
return _totalCheckpoints.lowerLookup(key);
}

function upperLookup(uint96 key) public view returns (uint160) {
return _totalCheckpoints.upperLookup(key);
}

function upperLookupRecent(uint96 key) public view returns (uint224) {
return _totalCheckpoints.upperLookupRecent(key);
}

function length() public view returns (uint256) {
return _totalCheckpoints._checkpoints.length;
}
}
1 change: 1 addition & 0 deletions contracts/mocks/EnumerableMapMock.sol
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/EnumerableMapMock.js.

pragma solidity ^0.8.0;

Expand Down
1 change: 1 addition & 0 deletions contracts/mocks/EnumerableSetMock.sol
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/EnumerableSetMock.js.

pragma solidity ^0.8.0;

Expand Down
1 change: 1 addition & 0 deletions contracts/mocks/SafeCastMock.sol
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/SafeCastMock.js.

pragma solidity ^0.8.0;

Expand Down
52 changes: 50 additions & 2 deletions contracts/utils/Arrays.sol
Expand Up @@ -3,12 +3,15 @@

pragma solidity ^0.8.0;

import "./StorageSlot.sol";
import "./math/Math.sol";

/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using StorageSlot for bytes32;

/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
Expand All @@ -31,18 +34,63 @@ library Arrays {

// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (array[mid] > element) {
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}

// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && array[low - 1] == element) {
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}

/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getAddressSlot();
}

/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getBytes32Slot();
}

/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
/// @solidity memory-safe-assembly
assembly {
mstore(0, arr.slot)
slot := add(keccak256(0, 0x20), pos)
}
return slot.getUint256Slot();
}
}

0 comments on commit 71aaca2

Please sign in to comment.