-
Notifications
You must be signed in to change notification settings - Fork 0
/
mem.sol
167 lines (157 loc) · 4.67 KB
/
mem.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/*
* These functions are VERY DANGEROUS!
* They operate directly on memory pointers, use with caution.
*
* Assembly here is marked as memory-safe for optimization.
* The caller MUST use pointers in a memory-safe way!
* https://docs.soliditylang.org/en/latest/assembly.html#memory-safety
*/
/**
* @dev Load 1 byte from the pointer.
* The result is in the least significant byte, hence uint8.
*/
function mload8(uint256 ptr) pure returns (uint8 item) {
/// @solidity memory-safe-assembly
assembly {
item := byte(0, mload(ptr))
}
return item;
}
/**
* @dev Copy `n` memory bytes.
* WARNING: Does not handle pointer overlap!
*/
function memcpy(uint256 ptrDest, uint256 ptrSrc, uint256 length) pure {
// copy 32-byte chunks
while (length >= 32) {
/// @solidity memory-safe-assembly
assembly {
mstore(ptrDest, mload(ptrSrc))
}
// safe because total addition will be <= length (ptr+len is implicitly safe)
unchecked {
ptrDest += 32;
ptrSrc += 32;
length -= 32;
}
}
// copy the 0-31 length tail
// (the rest is an inlined `mstoreN`)
uint256 mask = leftMask(length);
/// @solidity memory-safe-assembly
assembly {
mstore(ptrDest,
or(
// store the left part
and(mload(ptrSrc), mask),
// preserve the right part
and(mload(ptrDest), not(mask))
)
)
}
}
/**
* @dev mstore `n` bytes (left-aligned) of `data`
*/
function mstoreN(uint256 ptrDest, bytes32 data, uint256 n) pure {
uint256 mask = leftMask(n);
/// @solidity memory-safe-assembly
assembly {
mstore(ptrDest,
or(
// store the left part
and(data, mask),
// preserve the right part
and(mload(ptrDest), not(mask))
)
)
}
}
/**
* @dev Copy `n` memory bytes using identity precompile.
*/
function memmove(uint256 ptrDest, uint256 ptrSrc, uint256 n) view {
/// @solidity memory-safe-assembly
assembly {
pop(
staticcall(
gas(), // gas (unused is returned)
0x04, // identity precompile address
ptrSrc, // argsOffset
n, // argsSize: byte size to copy
ptrDest, // retOffset
n // retSize: byte size to copy
)
)
}
}
/**
* @dev Compare `n` memory bytes lexicographically.
* Returns 0 for equal, < 0 for less than and > 0 for greater than.
*
* https://doc.rust-lang.org/std/cmp/trait.Ord.html#lexicographical-comparison
*/
function memcmp(uint256 ptrSelf, uint256 ptrOther, uint256 n) pure returns (int256) {
// binary search for the first inequality
while (n >= 32) {
// safe because total addition will be <= n (ptr+len is implicitly safe)
unchecked {
uint256 nHalf = n / 2;
if (memeq(ptrSelf, ptrOther, nHalf)) {
ptrSelf += nHalf;
ptrOther += nHalf;
// (can't do n /= 2 instead of nHalf, some bytes would be skipped)
n -= nHalf;
// an explicit continue is better for optimization here
continue;
} else {
n -= nHalf;
}
}
}
uint256 mask = leftMask(n);
int256 diff;
/// @solidity memory-safe-assembly
assembly {
// for <32 bytes subtraction can be used for comparison,
// just need to shift away from MSB
diff := sub(
shr(8, and(mload(ptrSelf), mask)),
shr(8, and(mload(ptrOther), mask))
)
}
return diff;
}
/**
* @dev Returns true if `n` memory bytes are equal.
*
* It's faster (up to 4x) than memcmp, especially on medium byte lengths like 32-320.
* The benefit gets smaller for larger lengths, for 10000 it's only 30% faster.
*/
function memeq(uint256 ptrSelf, uint256 ptrOther, uint256 n) pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(ptrSelf, n), keccak256(ptrOther, n))
}
}
/**
* @dev Left-aligned byte mask (e.g. for partial mload/mstore).
* For length >= 32 returns type(uint256).max
*
* length 0: 0x000000...000000
* length 1: 0xff0000...000000
* length 2: 0xffff00...000000
* ...
* length 30: 0xffffff...ff0000
* length 31: 0xffffff...ffff00
* length 32+: 0xffffff...ffffff
*/
function leftMask(uint256 length) pure returns (uint256) {
unchecked {
return ~(
type(uint256).max >> (length * 8)
);
}
}