@@ -206,6 +206,193 @@ library Checkpoints {
206
206
}
207
207
}
208
208
209
+ struct Trace208 {
210
+ Checkpoint208[] _checkpoints;
211
+ }
212
+
213
+ struct Checkpoint208 {
214
+ uint48 _key;
215
+ uint208 _value;
216
+ }
217
+
218
+ /**
219
+ * @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.
220
+ *
221
+ * Returns previous value and new value.
222
+ *
223
+ * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the library.
224
+ */
225
+ function push (Trace208 storage self , uint48 key , uint208 value ) internal returns (uint208 , uint208 ) {
226
+ return _insert (self._checkpoints, key, value);
227
+ }
228
+
229
+ /**
230
+ * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if there is none.
231
+ */
232
+ function lowerLookup (Trace208 storage self , uint48 key ) internal view returns (uint208 ) {
233
+ uint256 len = self._checkpoints.length ;
234
+ uint256 pos = _lowerBinaryLookup (self._checkpoints, key, 0 , len);
235
+ return pos == len ? 0 : _unsafeAccess (self._checkpoints, pos)._value;
236
+ }
237
+
238
+ /**
239
+ * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none.
240
+ */
241
+ function upperLookup (Trace208 storage self , uint48 key ) internal view returns (uint208 ) {
242
+ uint256 len = self._checkpoints.length ;
243
+ uint256 pos = _upperBinaryLookup (self._checkpoints, key, 0 , len);
244
+ return pos == 0 ? 0 : _unsafeAccess (self._checkpoints, pos - 1 )._value;
245
+ }
246
+
247
+ /**
248
+ * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero if there is none.
249
+ *
250
+ * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high keys).
251
+ */
252
+ function upperLookupRecent (Trace208 storage self , uint48 key ) internal view returns (uint208 ) {
253
+ uint256 len = self._checkpoints.length ;
254
+
255
+ uint256 low = 0 ;
256
+ uint256 high = len;
257
+
258
+ if (len > 5 ) {
259
+ uint256 mid = len - Math.sqrt (len);
260
+ if (key < _unsafeAccess (self._checkpoints, mid)._key) {
261
+ high = mid;
262
+ } else {
263
+ low = mid + 1 ;
264
+ }
265
+ }
266
+
267
+ uint256 pos = _upperBinaryLookup (self._checkpoints, key, low, high);
268
+
269
+ return pos == 0 ? 0 : _unsafeAccess (self._checkpoints, pos - 1 )._value;
270
+ }
271
+
272
+ /**
273
+ * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
274
+ */
275
+ function latest (Trace208 storage self ) internal view returns (uint208 ) {
276
+ uint256 pos = self._checkpoints.length ;
277
+ return pos == 0 ? 0 : _unsafeAccess (self._checkpoints, pos - 1 )._value;
278
+ }
279
+
280
+ /**
281
+ * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
282
+ * in the most recent checkpoint.
283
+ */
284
+ function latestCheckpoint (Trace208 storage self ) internal view returns (bool exists , uint48 _key , uint208 _value ) {
285
+ uint256 pos = self._checkpoints.length ;
286
+ if (pos == 0 ) {
287
+ return (false , 0 , 0 );
288
+ } else {
289
+ Checkpoint208 memory ckpt = _unsafeAccess (self._checkpoints, pos - 1 );
290
+ return (true , ckpt._key, ckpt._value);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * @dev Returns the number of checkpoint.
296
+ */
297
+ function length (Trace208 storage self ) internal view returns (uint256 ) {
298
+ return self._checkpoints.length ;
299
+ }
300
+
301
+ /**
302
+ * @dev Returns checkpoint at given position.
303
+ */
304
+ function at (Trace208 storage self , uint32 pos ) internal view returns (Checkpoint208 memory ) {
305
+ return self._checkpoints[pos];
306
+ }
307
+
308
+ /**
309
+ * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
310
+ * or by updating the last one.
311
+ */
312
+ function _insert (Checkpoint208[] storage self , uint48 key , uint208 value ) private returns (uint208 , uint208 ) {
313
+ uint256 pos = self.length ;
314
+
315
+ if (pos > 0 ) {
316
+ // Copying to memory is important here.
317
+ Checkpoint208 memory last = _unsafeAccess (self, pos - 1 );
318
+
319
+ // Checkpoint keys must be non-decreasing.
320
+ if (last._key > key) {
321
+ revert CheckpointUnorderedInsertion ();
322
+ }
323
+
324
+ // Update or push new checkpoint
325
+ if (last._key == key) {
326
+ _unsafeAccess (self, pos - 1 )._value = value;
327
+ } else {
328
+ self.push (Checkpoint208 ({_key: key, _value: value}));
329
+ }
330
+ return (last._value, value);
331
+ } else {
332
+ self.push (Checkpoint208 ({_key: key, _value: value}));
333
+ return (0 , value);
334
+ }
335
+ }
336
+
337
+ /**
338
+ * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` if there is none.
339
+ * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
340
+ *
341
+ * WARNING: `high` should not be greater than the array's length.
342
+ */
343
+ function _upperBinaryLookup (
344
+ Checkpoint208[] storage self ,
345
+ uint48 key ,
346
+ uint256 low ,
347
+ uint256 high
348
+ ) private view returns (uint256 ) {
349
+ while (low < high) {
350
+ uint256 mid = Math.average (low, high);
351
+ if (_unsafeAccess (self, mid)._key > key) {
352
+ high = mid;
353
+ } else {
354
+ low = mid + 1 ;
355
+ }
356
+ }
357
+ return high;
358
+ }
359
+
360
+ /**
361
+ * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or `high` if there is none.
362
+ * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`.
363
+ *
364
+ * WARNING: `high` should not be greater than the array's length.
365
+ */
366
+ function _lowerBinaryLookup (
367
+ Checkpoint208[] storage self ,
368
+ uint48 key ,
369
+ uint256 low ,
370
+ uint256 high
371
+ ) private view returns (uint256 ) {
372
+ while (low < high) {
373
+ uint256 mid = Math.average (low, high);
374
+ if (_unsafeAccess (self, mid)._key < key) {
375
+ low = mid + 1 ;
376
+ } else {
377
+ high = mid;
378
+ }
379
+ }
380
+ return high;
381
+ }
382
+
383
+ /**
384
+ * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
385
+ */
386
+ function _unsafeAccess (
387
+ Checkpoint208[] storage self ,
388
+ uint256 pos
389
+ ) private pure returns (Checkpoint208 storage result ) {
390
+ assembly {
391
+ mstore (0 , self.slot)
392
+ result.slot := add (keccak256 (0 , 0x20 ), pos)
393
+ }
394
+ }
395
+
209
396
struct Trace160 {
210
397
Checkpoint160[] _checkpoints;
211
398
}
0 commit comments