Skip to content

Commit 6bb96d5

Browse files
committed
WIP: Add more Memory functions
1 parent 6094bb7 commit 6bb96d5

File tree

5 files changed

+121
-10
lines changed

5 files changed

+121
-10
lines changed

contracts/utils/Bytes.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ library Bytes {
9999
return result;
100100
}
101101

102+
/**
103+
* @dev Returns true if the two byte buffers are equal.
104+
*/
105+
function equal(bytes memory a, bytes memory b) internal pure returns (bool) {
106+
return a.length == b.length && keccak256(a) == keccak256(b);
107+
}
108+
102109
/**
103110
* @dev Reads a bytes32 from a bytes array without bounds checking.
104111
*

contracts/utils/Memory.sol

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ pragma solidity ^0.8.20;
1414
library Memory {
1515
type Pointer is bytes32;
1616

17-
/// @dev Returns a memory pointer to the current free memory pointer.
17+
/// @dev Returns a `Pointer` to the current free `Pointer`.
1818
function getFreePointer() internal pure returns (Pointer ptr) {
1919
assembly ("memory-safe") {
2020
ptr := mload(0x40)
2121
}
2222
}
2323

24-
/// @dev Sets the free memory pointer to a specific value.
24+
/// @dev Sets the free `Pointer` to a specific value.
2525
///
2626
/// WARNING: Everything after the pointer may be overwritten.
2727
function setFreePointer(Pointer ptr) internal pure {
@@ -30,13 +30,71 @@ library Memory {
3030
}
3131
}
3232

33-
/// @dev Pointer to `bytes32`.
33+
/// @dev Returns a `Pointer` to the content of a `bytes` buffer. Skips the length word.
34+
function contentPointer(bytes memory buffer) internal pure returns (Pointer) {
35+
return addOffset(asPointer(buffer), 32);
36+
}
37+
38+
/**
39+
* @dev Copies `length` bytes from `srcPtr` to `destPtr`. Equivalent to https://www.evm.codes/?fork=cancun#5e[`mcopy`].
40+
*
41+
* WARNING: Reading or writing beyond the allocated memory bounds of either pointer
42+
* will result in undefined behavior and potential memory corruption.
43+
*/
44+
function copy(Pointer destPtr, Pointer srcPtr, uint256 length) internal pure {
45+
assembly ("memory-safe") {
46+
mcopy(destPtr, srcPtr, length)
47+
}
48+
}
49+
50+
/// @dev Extracts a `bytes1` from a `Pointer`. `offset` starts from the most significant byte.
51+
function extractByte(Pointer ptr, uint256 offset) internal pure returns (bytes1 v) {
52+
bytes32 word = extractWord(ptr);
53+
assembly ("memory-safe") {
54+
v := byte(offset, word)
55+
}
56+
}
57+
58+
/// @dev Extracts a `bytes32` from a `Pointer`.
59+
function extractWord(Pointer ptr) internal pure returns (bytes32 v) {
60+
assembly ("memory-safe") {
61+
v := mload(ptr)
62+
}
63+
}
64+
65+
/// @dev Adds an offset to a `Pointer`.
66+
function addOffset(Pointer ptr, uint256 offset) internal pure returns (Pointer) {
67+
return asPointer(bytes32(asUint256(ptr) + offset));
68+
}
69+
70+
/// @dev `Pointer` to `bytes32`.
3471
function asBytes32(Pointer ptr) internal pure returns (bytes32) {
3572
return Pointer.unwrap(ptr);
3673
}
3774

38-
/// @dev `bytes32` to pointer.
75+
/// @dev `Pointer` to `uint256`.
76+
function asUint256(Pointer ptr) internal pure returns (uint256) {
77+
return uint256(asBytes32(ptr));
78+
}
79+
80+
/// @dev `bytes32` to `Pointer`.
3981
function asPointer(bytes32 value) internal pure returns (Pointer) {
4082
return Pointer.wrap(value);
4183
}
84+
85+
/// @dev `bytes` to `Pointer`.
86+
function asPointer(bytes memory value) internal pure returns (Pointer) {
87+
bytes32 ptr;
88+
assembly ("memory-safe") {
89+
ptr := value
90+
}
91+
return asPointer(ptr);
92+
}
93+
94+
/// @dev `Pointer` to `bytes`.
95+
function asBytes(Pointer ptr) internal pure returns (bytes memory b) {
96+
assembly ("memory-safe") {
97+
b := ptr
98+
}
99+
}
42100
}

contracts/utils/Strings.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pragma solidity ^0.8.20;
66
import {Math} from "./math/Math.sol";
77
import {SafeCast} from "./math/SafeCast.sol";
88
import {SignedMath} from "./math/SignedMath.sol";
9+
import {Bytes} from "./Bytes.sol";
910

1011
/**
1112
* @dev String operations.
@@ -132,7 +133,7 @@ library Strings {
132133
* @dev Returns true if the two strings are equal.
133134
*/
134135
function equal(string memory a, string memory b) internal pure returns (bool) {
135-
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
136+
return Bytes.equal(bytes(a), bytes(b));
136137
}
137138

138139
/**

test/utils/Bytes.t.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {Test} from "forge-std/Test.sol";
6+
import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol";
7+
8+
contract BytesTest is Test {
9+
function testSymbolicEqual(bytes memory a, bytes memory b) public pure {
10+
assertEq(Bytes.equal(a, b), Bytes.equal(a, b));
11+
}
12+
}

test/utils/Memory.t.sol

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,49 @@ pragma solidity ^0.8.20;
44

55
import {Test} from "forge-std/Test.sol";
66
import {Memory} from "@openzeppelin/contracts/utils/Memory.sol";
7+
import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol";
78

89
contract MemoryTest is Test {
910
using Memory for *;
1011

11-
function testSymbolicGetSetFreePointer(uint256 seed) public pure {
12-
// - first 0x80 bytes are reserved (scratch + FMP + zero)
13-
// - moving the free memory pointer to far causes OOG errors
14-
bytes32 ptr = bytes32(bound(seed, 0x80, type(uint24).max));
12+
// - first 0x80 bytes are reserved (scratch + FMP + zero)
13+
uint256 constant START_PTR = 0x80;
14+
// - moving the free memory pointer to far causes OOG errors
15+
uint256 constant END_PTR = type(uint24).max;
1516

16-
Memory.setFreePointer(ptr.asPointer());
17+
function testGetSetFreePointer(uint256 seed) public pure {
18+
bytes32 ptr = bytes32(bound(seed, START_PTR, END_PTR));
19+
ptr.asPointer().setFreePointer();
1720
assertEq(Memory.getFreePointer().asBytes32(), ptr);
1821
}
22+
23+
function testSymbolicContentPointer(uint256 seed) public pure {
24+
Memory.Pointer ptr = bytes32(bound(seed, START_PTR, END_PTR)).asPointer();
25+
assertEq(ptr.asBytes().contentPointer().asBytes32(), ptr.addOffset(32).asBytes32());
26+
}
27+
28+
// function testCopy(bytes memory data, uint256 destSeed) public pure {
29+
// uint256 upperPtr = data.asPointer().asUint256() + data.length;
30+
// Memory.Pointer destPtr = bytes32(bound(destSeed, upperPtr, upperPtr + 100)).asPointer();
31+
// Memory.copy(data.asPointer(), destPtr, data.length + 32);
32+
// for (uint256 i = 0; i < data.length; i++) {
33+
// assertEq(data[i], destPtr.asBytes()[i]);
34+
// }
35+
// }
36+
37+
function testExtractByte(uint256 seed, uint256 index) public pure {
38+
index = bound(index, 0, 31);
39+
Memory.Pointer ptr = bytes32(bound(seed, START_PTR, END_PTR)).asPointer();
40+
assertEq(ptr.extractByte(index), bytes1(ptr.asBytes32() >> (256 - index * 8)));
41+
}
42+
43+
// function testExtractWord(uint256 seed) public pure {
44+
// Memory.Pointer ptr = bytes32(bound(seed, START_PTR, END_PTR)).asPointer();
45+
// assertEq(ptr.extractWord(), ptr.asBytes32());
46+
// }
47+
48+
// function testAddOffset(uint256 seed, uint256 offset) public pure {
49+
// Memory.Pointer ptr = bytes32(bound(seed, START_PTR, END_PTR)).asPointer();
50+
// assertEq(ptr.addOffset(offset).asUint256(), ptr.asUint256() + offset);
51+
// }
1952
}

0 commit comments

Comments
 (0)