Skip to content

Commit 59b1654

Browse files
KyrylRArvolear
andauthored
Fixed unsafeCopy() function. Added more getters for pointers. (#101)
* Fixed `unsafeCopy()` function. Added more getters for pointers. * simplify a bit --------- Co-authored-by: Artem Chystiakov <artem.ch31@gmail.com>
1 parent af7e26e commit 59b1654

File tree

5 files changed

+95
-63
lines changed

5 files changed

+95
-63
lines changed

contracts/libs/utils/MemoryUtils.sol

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,29 @@ pragma solidity ^0.8.4;
77
*/
88
library MemoryUtils {
99
/**
10-
* @notice Copies the contents of the source string to the destination string.
10+
* @notice Copies the contents of the source bytes to the destination bytes. strings can be casted
11+
* to bytes in order to use this function.
1112
*
12-
* @param source_ The source string to copy from.
13-
* @return destination_ The newly allocated string.
13+
* @param source_ The source bytes to copy from.
14+
* @return destination_ The newly allocated bytes.
1415
*/
15-
function copy(string memory source_) internal view returns (string memory destination_) {
16-
destination_ = new string(bytes(source_).length);
16+
function copy(bytes memory source_) internal view returns (bytes memory destination_) {
17+
destination_ = new bytes(source_.length);
1718

18-
unsafeMemoryCopy(getPointer(source_), getPointer(destination_), bytes(source_).length);
19+
unsafeCopy(getDataPointer(source_), getDataPointer(destination_), source_.length);
1920
}
2021

2122
/**
22-
* @notice Copies the contents of the source bytes to the destination bytes.
23+
* @notice Copies the contents of the source bytes32 array to the destination bytes32 array.
24+
* uint256[], address[] array can be casted to bytes32[] via `TypeCaster` library.
2325
*
24-
* @param source_ The source bytes to copy from.
25-
* @return destination_ The newly allocated bytes.
26+
* @param source_ The source bytes32 array to copy from.
27+
* @return destination_ The newly allocated bytes32 array.
2628
*/
27-
function copy(bytes memory source_) internal view returns (bytes memory destination_) {
28-
destination_ = new bytes(source_.length);
29+
function copy(bytes32[] memory source_) internal view returns (bytes32[] memory destination_) {
30+
destination_ = new bytes32[](source_.length);
2931

30-
unsafeMemoryCopy(getPointer(source_), getPointer(destination_), source_.length);
32+
unsafeCopy(getDataPointer(source_), getDataPointer(destination_), source_.length * 32);
3133
}
3234

3335
/**
@@ -41,40 +43,51 @@ library MemoryUtils {
4143
* This signature of calling identity precompile is:
4244
* staticcall(gas(), address(0x04), argsOffset, argsSize, retOffset, retSize)
4345
*/
44-
function unsafeMemoryCopy(
46+
function unsafeCopy(
4547
uint256 sourcePointer_,
4648
uint256 destinationPointer_,
4749
uint256 size_
4850
) internal view {
4951
assembly {
50-
pop(
51-
staticcall(
52-
gas(),
53-
4,
54-
add(sourcePointer_, 32),
55-
size_,
56-
add(destinationPointer_, 32),
57-
size_
58-
)
59-
)
52+
pop(staticcall(gas(), 4, sourcePointer_, size_, destinationPointer_, size_))
53+
}
54+
}
55+
56+
/**
57+
* @notice Returns the memory pointer to the given bytes starting position including the length.
58+
*/
59+
function getPointer(bytes memory data_) internal pure returns (uint256 pointer_) {
60+
assembly {
61+
pointer_ := data_
62+
}
63+
}
64+
65+
/**
66+
* @notice Returns the memory pointer to the given bytes starting position including the length.
67+
* Cast uint256[] and address[] to bytes32[] via `TypeCaster` library.
68+
*/
69+
function getPointer(bytes32[] memory data_) internal pure returns (uint256 pointer_) {
70+
assembly {
71+
pointer_ := data_
6072
}
6173
}
6274

6375
/**
64-
* @notice Returns the memory pointer of the given bytes data.
76+
* @notice Returns the memory pointer to the given bytes data starting position skipping the length.
6577
*/
66-
function getPointer(bytes memory data) internal pure returns (uint256 pointer) {
78+
function getDataPointer(bytes memory data_) internal pure returns (uint256 pointer_) {
6779
assembly {
68-
pointer := data
80+
pointer_ := add(data_, 32)
6981
}
7082
}
7183

7284
/**
73-
* @notice Returns the memory pointer of the given string data.
85+
* @notice Returns the memory pointer to the given bytes data starting position skipping the length.
86+
* Cast uint256[] and address[] to bytes32[] via `TypeCaster` library.
7487
*/
75-
function getPointer(string memory data) internal pure returns (uint256 pointer) {
88+
function getDataPointer(bytes32[] memory data_) internal pure returns (uint256 pointer_) {
7689
assembly {
77-
pointer := data
90+
pointer_ := add(data_, 32)
7891
}
7992
}
8093
}

contracts/mock/libs/utils/MemoryUtilsMock.sol

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,95 @@
22
pragma solidity ^0.8.4;
33

44
import {MemoryUtils} from "../../../libs/utils/MemoryUtils.sol";
5+
import {TypeCaster} from "../../../libs/utils/TypeCaster.sol";
56

67
contract MemoryUtilsMock {
8+
using TypeCaster for *;
79
using MemoryUtils for *;
810

9-
function testStringMemoryCopy(string memory data_) external view {
10-
string memory someString = new string(bytes(data_).length);
11+
function testBytesMemoryCopy(bytes memory data_) external view {
12+
bytes memory someBytes_ = new bytes(data_.length);
1113

1214
require(
13-
keccak256(bytes(data_)) != keccak256(bytes(someString)),
14-
"MemoryUtilsMock: testStringMemoryCopy failed. Initial data and someString are equal"
15+
keccak256(data_) != keccak256(someBytes_),
16+
"MemoryUtilsMock: testBytesMemoryCopy failed. Initial data and someBytes are equal"
1517
);
1618

17-
someString = data_.copy();
19+
someBytes_ = data_.copy();
1820

1921
require(
20-
keccak256(bytes(data_)) == keccak256(bytes(someString)),
21-
"MemoryUtilsMock: testStringMemoryCopy failed. Initial data and someString are not equal"
22+
keccak256(data_) == keccak256(someBytes_),
23+
"MemoryUtilsMock: testBytesMemoryCopy failed. Initial data and someBytes are not equal"
2224
);
2325
}
2426

25-
function testBytesMemoryCopy(bytes memory data_) external view {
26-
bytes memory someBytes = new bytes(data_.length);
27+
function testBytes32MemoryCopy(bytes32[] memory data_) external view {
28+
bytes32[] memory someBytes_ = new bytes32[](data_.length);
2729

2830
require(
29-
keccak256(data_) != keccak256(someBytes),
30-
"MemoryUtilsMock: testBytesMemoryCopy failed. Initial data and someBytes are equal"
31+
keccak256(abi.encode(data_)) != keccak256(abi.encode(someBytes_)),
32+
"MemoryUtilsMock: testBytes32MemoryCopy failed. Initial data and someBytes are equal"
3133
);
3234

33-
someBytes = data_.copy();
35+
someBytes_ = data_.copy();
3436

3537
require(
36-
keccak256(data_) == keccak256(someBytes),
37-
"MemoryUtilsMock: testBytesMemoryCopy failed. Initial data and someBytes are not equal"
38+
keccak256(abi.encode(data_)) == keccak256(abi.encode(someBytes_)),
39+
"MemoryUtilsMock: testBytes32MemoryCopy failed. Initial data and someBytes are not equal"
3840
);
3941
}
4042

4143
function testUnsafeMemoryCopy(bytes memory data_) external view {
42-
bytes memory someBytes = new bytes(data_.length);
44+
bytes memory someBytes_ = new bytes(data_.length);
4345

4446
require(
45-
keccak256(data_) != keccak256(someBytes),
47+
keccak256(data_) != keccak256(someBytes_),
4648
"MemoryUtilsMock: testBigMemory failed. Initial data and someBytes are equal"
4749
);
4850

49-
MemoryUtils.unsafeMemoryCopy(
50-
MemoryUtils.getPointer(someBytes),
51-
MemoryUtils.getPointer(data_),
52-
someBytes.length
51+
MemoryUtils.unsafeCopy(
52+
MemoryUtils.getDataPointer(someBytes_),
53+
MemoryUtils.getDataPointer(data_),
54+
someBytes_.length
5355
);
5456

5557
require(
56-
keccak256(data_) == keccak256(someBytes),
58+
keccak256(data_) == keccak256(someBytes_),
5759
"MemoryUtilsMock: testBigMemory failed. Initial data and someBytes are not equal"
5860
);
5961
}
6062

6163
function testPartialCopy(bytes memory data_) external view {
62-
bytes memory someBytes = new bytes(data_.length / 2);
64+
bytes memory someBytes_ = new bytes(data_.length / 2);
6365

6466
require(
65-
keccak256(data_) != keccak256(someBytes),
67+
keccak256(data_) != keccak256(someBytes_),
6668
"MemoryUtilsMock: testPartialCopy failed. Initial data and someBytes are equal"
6769
);
6870

69-
MemoryUtils.unsafeMemoryCopy(
70-
MemoryUtils.getPointer(someBytes),
71-
MemoryUtils.getPointer(data_),
72-
someBytes.length
71+
MemoryUtils.unsafeCopy(
72+
MemoryUtils.getDataPointer(someBytes_),
73+
MemoryUtils.getDataPointer(data_),
74+
someBytes_.length
7375
);
7476

75-
for (uint256 i = 0; i < someBytes.length; i++) {
77+
for (uint256 i = 0; i < someBytes_.length; i++) {
7678
require(
77-
someBytes[i] == data_[i],
79+
someBytes_[i] == data_[i],
7880
"MemoryUtilsMock: testPartialCopy failed. Initial data and someBytes are not equal"
7981
);
8082
}
8183
}
84+
85+
/**
86+
* @dev Since the underlying logic of `getPointer()/getDataPointer()` is only specific to EVMs,
87+
* we only do a simple mock test for coverage.
88+
*/
89+
function testForCoverage() external pure {
90+
MemoryUtils.getPointer(new bytes(1));
91+
MemoryUtils.getPointer((new uint256[](1)).asBytes32Array());
92+
93+
MemoryUtils.getDataPointer(new bytes(1));
94+
MemoryUtils.getDataPointer((new uint256[](1)).asBytes32Array());
95+
}
8296
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@solarity/solidity-lib",
3-
"version": "2.7.5",
3+
"version": "2.7.6",
44
"license": "MIT",
55
"author": "Distributed Lab",
66
"readme": "README.md",

test/libs/utils/MemoryUtils.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ describe("MemoryUtils", () => {
2424
await expect(mock.testBytesMemoryCopy(ethers.randomBytes(20))).to.be.eventually.fulfilled;
2525
});
2626

27-
it("should copy arbitrary chunks of memory (string)", async () => {
28-
await expect(mock.testStringMemoryCopy("Hello, world!")).to.be.eventually.fulfilled;
27+
it("should copy arbitrary chunks of memory (bytes32[])", async () => {
28+
await expect(mock.testBytes32MemoryCopy([ethers.randomBytes(32), ethers.randomBytes(32)])).to.be.eventually
29+
.fulfilled;
2930
});
3031

3132
it("should copy 20 bytes of memory", async () => {
@@ -47,5 +48,9 @@ describe("MemoryUtils", () => {
4748
it("should copy partial chunks of memory", async () => {
4849
await expect(mock.testPartialCopy(ethers.randomBytes(15))).to.be.eventually.fulfilled;
4950
});
51+
52+
it("should cover getter functions", async () => {
53+
await expect(mock.testForCoverage()).to.be.eventually.fulfilled;
54+
});
5055
});
5156
});

0 commit comments

Comments
 (0)