@@ -19,6 +19,209 @@ library Checkpoints {
19
19
*/
20
20
error CheckpointUnorderedInsertion ();
21
21
22
+ struct Trace256 {
23
+ Checkpoint256[] _checkpoints;
24
+ }
25
+
26
+ struct Checkpoint256 {
27
+ uint256 _key;
28
+ uint256 _value;
29
+ }
30
+
31
+ /**
32
+ * @dev Pushes a (`key`, `value`) pair into a Trace256 so that it is stored as the checkpoint.
33
+ *
34
+ * Returns previous value and new value.
35
+ *
36
+ * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint256).max` key set will disable the
37
+ * library.
38
+ */
39
+ function push (
40
+ Trace256 storage self ,
41
+ uint256 key ,
42
+ uint256 value
43
+ ) internal returns (uint256 oldValue , uint256 newValue ) {
44
+ return _insert (self._checkpoints, key, value);
45
+ }
46
+
47
+ /**
48
+ * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
49
+ * there is none.
50
+ */
51
+ function lowerLookup (Trace256 storage self , uint256 key ) internal view returns (uint256 ) {
52
+ uint256 len = self._checkpoints.length ;
53
+ uint256 pos = _lowerBinaryLookup (self._checkpoints, key, 0 , len);
54
+ return pos == len ? 0 : _unsafeAccess (self._checkpoints, pos)._value;
55
+ }
56
+
57
+ /**
58
+ * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
59
+ * if there is none.
60
+ */
61
+ function upperLookup (Trace256 storage self , uint256 key ) internal view returns (uint256 ) {
62
+ uint256 len = self._checkpoints.length ;
63
+ uint256 pos = _upperBinaryLookup (self._checkpoints, key, 0 , len);
64
+ return pos == 0 ? 0 : _unsafeAccess (self._checkpoints, pos - 1 )._value;
65
+ }
66
+
67
+ /**
68
+ * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
69
+ * if there is none.
70
+ *
71
+ * NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high
72
+ * keys).
73
+ */
74
+ function upperLookupRecent (Trace256 storage self , uint256 key ) internal view returns (uint256 ) {
75
+ uint256 len = self._checkpoints.length ;
76
+
77
+ uint256 low = 0 ;
78
+ uint256 high = len;
79
+
80
+ if (len > 5 ) {
81
+ uint256 mid = len - Math.sqrt (len);
82
+ if (key < _unsafeAccess (self._checkpoints, mid)._key) {
83
+ high = mid;
84
+ } else {
85
+ low = mid + 1 ;
86
+ }
87
+ }
88
+
89
+ uint256 pos = _upperBinaryLookup (self._checkpoints, key, low, high);
90
+
91
+ return pos == 0 ? 0 : _unsafeAccess (self._checkpoints, pos - 1 )._value;
92
+ }
93
+
94
+ /**
95
+ * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
96
+ */
97
+ function latest (Trace256 storage self ) internal view returns (uint256 ) {
98
+ uint256 pos = self._checkpoints.length ;
99
+ return pos == 0 ? 0 : _unsafeAccess (self._checkpoints, pos - 1 )._value;
100
+ }
101
+
102
+ /**
103
+ * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
104
+ * in the most recent checkpoint.
105
+ */
106
+ function latestCheckpoint (Trace256 storage self ) internal view returns (bool exists , uint256 _key , uint256 _value ) {
107
+ uint256 pos = self._checkpoints.length ;
108
+ if (pos == 0 ) {
109
+ return (false , 0 , 0 );
110
+ } else {
111
+ Checkpoint256 storage ckpt = _unsafeAccess (self._checkpoints, pos - 1 );
112
+ return (true , ckpt._key, ckpt._value);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @dev Returns the number of checkpoints.
118
+ */
119
+ function length (Trace256 storage self ) internal view returns (uint256 ) {
120
+ return self._checkpoints.length ;
121
+ }
122
+
123
+ /**
124
+ * @dev Returns checkpoint at given position.
125
+ */
126
+ function at (Trace256 storage self , uint32 pos ) internal view returns (Checkpoint256 memory ) {
127
+ return self._checkpoints[pos];
128
+ }
129
+
130
+ /**
131
+ * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
132
+ * or by updating the last one.
133
+ */
134
+ function _insert (
135
+ Checkpoint256[] storage self ,
136
+ uint256 key ,
137
+ uint256 value
138
+ ) private returns (uint256 oldValue , uint256 newValue ) {
139
+ uint256 pos = self.length ;
140
+
141
+ if (pos > 0 ) {
142
+ Checkpoint256 storage last = _unsafeAccess (self, pos - 1 );
143
+ uint256 lastKey = last._key;
144
+ uint256 lastValue = last._value;
145
+
146
+ // Checkpoint keys must be non-decreasing.
147
+ if (lastKey > key) {
148
+ revert CheckpointUnorderedInsertion ();
149
+ }
150
+
151
+ // Update or push new checkpoint
152
+ if (lastKey == key) {
153
+ last._value = value;
154
+ } else {
155
+ self.push (Checkpoint256 ({_key: key, _value: value}));
156
+ }
157
+ return (lastValue, value);
158
+ } else {
159
+ self.push (Checkpoint256 ({_key: key, _value: value}));
160
+ return (0 , value);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
166
+ * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
167
+ * `high`.
168
+ *
169
+ * WARNING: `high` should not be greater than the array's length.
170
+ */
171
+ function _upperBinaryLookup (
172
+ Checkpoint256[] storage self ,
173
+ uint256 key ,
174
+ uint256 low ,
175
+ uint256 high
176
+ ) private view returns (uint256 ) {
177
+ while (low < high) {
178
+ uint256 mid = Math.average (low, high);
179
+ if (_unsafeAccess (self, mid)._key > key) {
180
+ high = mid;
181
+ } else {
182
+ low = mid + 1 ;
183
+ }
184
+ }
185
+ return high;
186
+ }
187
+
188
+ /**
189
+ * @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
190
+ * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
191
+ * `high`.
192
+ *
193
+ * WARNING: `high` should not be greater than the array's length.
194
+ */
195
+ function _lowerBinaryLookup (
196
+ Checkpoint256[] storage self ,
197
+ uint256 key ,
198
+ uint256 low ,
199
+ uint256 high
200
+ ) private view returns (uint256 ) {
201
+ while (low < high) {
202
+ uint256 mid = Math.average (low, high);
203
+ if (_unsafeAccess (self, mid)._key < key) {
204
+ low = mid + 1 ;
205
+ } else {
206
+ high = mid;
207
+ }
208
+ }
209
+ return high;
210
+ }
211
+
212
+ /**
213
+ * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
214
+ */
215
+ function _unsafeAccess (
216
+ Checkpoint256[] storage self ,
217
+ uint256 pos
218
+ ) private pure returns (Checkpoint256 storage result ) {
219
+ assembly {
220
+ mstore (0 , self.slot)
221
+ result.slot := add (keccak256 (0 , 0x20 ), mul (pos, 2 ))
222
+ }
223
+ }
224
+
22
225
struct Trace224 {
23
226
Checkpoint224[] _checkpoints;
24
227
}
0 commit comments