-
Notifications
You must be signed in to change notification settings - Fork 12.4k
Expand file tree
/
Copy pathMemory.sol
More file actions
154 lines (134 loc) · 6.07 KB
/
Memory.sol
File metadata and controls
154 lines (134 loc) · 6.07 KB
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
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.6.0) (utils/Memory.sol)
pragma solidity ^0.8.24;
import {Panic} from "./Panic.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Utilities to manipulate memory.
*
* Memory is a contiguous and dynamic byte array in which Solidity stores non-primitive types.
* This library provides functions to manipulate pointers to this dynamic array and work with slices of it.
*
* Slices provide a view into a portion of memory without copying data, enabling efficient substring operations.
*
* WARNING: When manipulating memory pointers or slices, make sure to follow the Solidity documentation
* guidelines for https://docs.soliditylang.org/en/v0.8.20/assembly.html#memory-safety[Memory Safety].
*/
library Memory {
type Pointer is bytes32;
/// @dev Returns a `Pointer` to the current free `Pointer`.
function getFreeMemoryPointer() internal pure returns (Pointer ptr) {
assembly ("memory-safe") {
ptr := mload(0x40)
}
}
/**
* @dev Sets the free `Pointer` to a specific value.
*
* The solidity memory layout requires that the FMP is never set to a value lower than 0x80. Setting the
* FMP to a value lower than 0x80 may cause unexpected behavior. Deallocating all memory can be achieved by
* setting the FMP to 0x80.
*
* WARNING: Everything after the pointer may be overwritten.
**/
function unsafeSetFreeMemoryPointer(Pointer ptr) internal pure {
assembly ("memory-safe") {
mstore(0x40, ptr)
}
}
/// @dev Move a pointer forward by a given offset.
function forward(Pointer ptr, uint256 offset) internal pure returns (Pointer) {
return Pointer.wrap(bytes32(uint256(Pointer.unwrap(ptr)) + offset));
}
/// @dev Equality comparator for memory pointers.
function equal(Pointer ptr1, Pointer ptr2) internal pure returns (bool) {
return Pointer.unwrap(ptr1) == Pointer.unwrap(ptr2);
}
type Slice is bytes32;
/// @dev Empty slice
function emptySlice() internal pure returns (Slice) {
return Slice.wrap(0);
}
/// @dev Get a slice representation of a bytes object in memory
function asSlice(bytes memory self) internal pure returns (Slice result) {
assembly ("memory-safe") {
result := or(shl(128, mload(self)), add(self, 0x20))
}
}
/// @dev Returns the length of a given slice (equiv to self.length for calldata slices)
function length(Slice self) internal pure returns (uint256 result) {
assembly ("memory-safe") {
result := shr(128, self)
}
}
/// @dev Offset a memory slice (equivalent to self[offset:] for calldata slices)
function slice(Slice self, uint256 offset) internal pure returns (Slice) {
if (offset > length(self)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
return _asSlice(length(self) - offset, forward(_pointer(self), offset));
}
/// @dev Offset and cut a Slice (equivalent to self[offset:offset+len] for calldata slices)
function slice(Slice self, uint256 offset, uint256 len) internal pure returns (Slice) {
if (offset + len > length(self)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
return _asSlice(len, forward(_pointer(self), offset));
}
/**
* @dev Read a bytes32 buffer from a given Slice at a specific offset
*
* NOTE: If offset > length(slice) - 0x20, part of the return value will be out of bound of the slice. These bytes are zeroed.
*/
function load(Slice self, uint256 offset) internal pure returns (bytes32 value) {
uint256 outOfBoundBytes = Math.saturatingSub(0x20 + offset, length(self));
if (outOfBoundBytes > 0x1f) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
assembly ("memory-safe") {
value := and(mload(add(and(self, shr(128, not(0))), offset)), shl(mul(8, outOfBoundBytes), not(0)))
}
}
/// @dev Extract the data corresponding to a Slice (allocate new memory)
function toBytes(Slice self) internal pure returns (bytes memory result) {
uint256 len = length(self);
Memory.Pointer ptr = _pointer(self);
assembly ("memory-safe") {
result := mload(0x40)
mstore(result, len)
mcopy(add(result, 0x20), ptr, len)
mstore(0x40, add(add(result, len), 0x20))
}
}
/// @dev Returns true if the two slices contain the same data.
function equal(Slice a, Slice b) internal pure returns (bool result) {
Memory.Pointer ptrA = _pointer(a);
Memory.Pointer ptrB = _pointer(b);
uint256 lenA = length(a);
uint256 lenB = length(b);
assembly ("memory-safe") {
result := eq(keccak256(ptrA, lenA), keccak256(ptrB, lenB))
}
}
/// @dev Returns true if the memory occupied by the slice is reserved (i.e. before the free memory pointer)
function isReserved(Slice self) internal pure returns (bool result) {
Memory.Pointer fmp = getFreeMemoryPointer();
Memory.Pointer end = forward(_pointer(self), length(self));
assembly ("memory-safe") {
result := iszero(lt(fmp, end)) // end <= fmp
}
}
/**
* @dev Private helper: create a slice from raw values (length and pointer)
*
* NOTE: this function MUST NOT be called with `len` or `ptr` that exceed `2**128-1`. This should never be
* the case of slices produced by `asSlice(bytes)`, and function that reduce the scope of slices
* (`slice(Slice,uint256)` and `slice(Slice,uint256, uint256)`) should not cause this issue if the parent slice is
* correct.
*/
function _asSlice(uint256 len, Memory.Pointer ptr) private pure returns (Slice result) {
assembly ("memory-safe") {
result := or(shl(128, len), ptr)
}
}
/// @dev Returns the memory location of a given slice (equiv to self.offset for calldata slices)
function _pointer(Slice self) private pure returns (Memory.Pointer result) {
assembly ("memory-safe") {
result := and(self, shr(128, not(0)))
}
}
}