Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dull-students-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`Memory`: Add library with utilities to manipulate memory
1 change: 1 addition & 0 deletions contracts/mocks/Stateless.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {SignatureChecker} from "../utils/cryptography/SignatureChecker.sol";
import {SignedMath} from "../utils/math/SignedMath.sol";
import {StorageSlot} from "../utils/StorageSlot.sol";
import {Strings} from "../utils/Strings.sol";
import {Memory} from "../utils/Memory.sol";
import {Time} from "../utils/types/Time.sol";

contract Dummy1234 {}
34 changes: 34 additions & 0 deletions contracts/utils/Memory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/// @dev Memory utility library.
library Memory {
type Pointer is bytes32;

/// @dev Returns a memory pointer to the current free memory pointer.
function getFreePointer() internal pure returns (Pointer ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
}
}

/// @dev Sets the free memory pointer to a specific value.
///
/// WARNING: Everything after the pointer may be overwritten.
function setFreePointer(Pointer ptr) internal pure {
assembly ("memory-safe") {
mstore(0x40, ptr)
}
}

/// @dev Pointer to `bytes32`.
function asBytes32(Pointer ptr) internal pure returns (bytes32) {
return Pointer.unwrap(ptr);
}

/// @dev `bytes32` to pointer.
function asPointer(bytes32 value) internal pure returns (Pointer) {
return Pointer.wrap(value);
}
}
1 change: 1 addition & 0 deletions contracts/utils/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Miscellaneous contracts and libraries containing utility functions you can use t
* {Packing}: A library for packing and unpacking multiple values into bytes32
* {Panic}: A library to revert with https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require[Solidity panic codes].
* {Comparators}: A library that contains comparator functions to use with with the {Heap} library.
* {Memory}: A utility library to manipulate memory.

[NOTE]
====
Expand Down
16 changes: 16 additions & 0 deletions test/utils/Memory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {Memory} from "@openzeppelin/contracts/utils/Memory.sol";

contract MemoryTest is Test {
using Memory for *;

function testSymbolicGetSetFreePointer(bytes32 ptr) public {
Memory.Pointer memoryPtr = ptr.asPointer();
Memory.setFreePointer(memoryPtr);
assertEq(Memory.getFreePointer().asBytes32(), memoryPtr.asBytes32());
}
}
41 changes: 41 additions & 0 deletions test/utils/Memory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');

async function fixture() {
const mock = await ethers.deployContract('$Memory');

return { mock };
}

describe('Memory', function () {
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});

describe('free pointer', function () {
it('sets memory pointer', async function () {
const ptr = '0x00000000000000000000000000000000000000000000000000000000000000a0';
expect(await this.mock.$setFreePointer(ptr)).to.not.be.reverted;
});

it('gets memory pointer', async function () {
expect(await this.mock.$getFreePointer()).to.equal(
// Default pointer
'0x0000000000000000000000000000000000000000000000000000000000000080',
);
});

it('asBytes32', async function () {
const ptr = ethers.toBeHex('0x1234', 32);
await this.mock.$setFreePointer(ptr);
expect(await this.mock.$asBytes32(ptr)).to.equal(ptr);
});

it('asPointer', async function () {
const ptr = ethers.toBeHex('0x1234', 32);
await this.mock.$setFreePointer(ptr);
expect(await this.mock.$asPointer(ptr)).to.equal(ptr);
});
});
});