@@ -3,6 +3,10 @@ pragma solidity ^0.8.0;
3
3
4
4
import "./BucketDLL.sol " ;
5
5
6
+ /// @title LogarithmicBuckets
7
+ /// @author Morpho Labs
8
+ /// @custom:contact security@morpho.xyz
9
+ /// @notice The logarithmic buckets data-structure.
6
10
library LogarithmicBuckets {
7
11
using BucketDLL for BucketDLL.List;
8
12
@@ -12,104 +16,108 @@ library LogarithmicBuckets {
12
16
uint256 bucketsMask;
13
17
}
14
18
15
- /// ERRORS // /
19
+ /* ERRORS * /
16
20
17
21
/// @notice Thrown when the address is zero at insertion.
18
22
error ZeroAddress ();
19
23
20
24
/// @notice Thrown when 0 value is inserted.
21
25
error ZeroValue ();
22
26
23
- /// INTERNAL // /
27
+ /* INTERNAL * /
24
28
25
- /// @notice Updates an account in the `_buckets `.
26
- /// @param _buckets The buckets to update.
27
- /// @param _id The address of the account.
28
- /// @param _newValue The new value of the account.
29
- /// @param _head Indicates whether to insert the new values at the head or at the tail of the buckets list.
29
+ /// @notice Updates an account in the `buckets `.
30
+ /// @param buckets The buckets to update.
31
+ /// @param id The address of the account.
32
+ /// @param newValue The new value of the account.
33
+ /// @param head Indicates whether to insert the new values at the head or at the tail of the buckets list.
30
34
function update (
31
- Buckets storage _buckets ,
32
- address _id ,
33
- uint256 _newValue ,
34
- bool _head
35
+ Buckets storage buckets ,
36
+ address id ,
37
+ uint256 newValue ,
38
+ bool head
35
39
) internal {
36
- if (_id == address (0 )) revert ZeroAddress ();
37
- uint256 value = _buckets .valueOf[_id ];
38
- _buckets .valueOf[_id ] = _newValue ;
40
+ if (id == address (0 )) revert ZeroAddress ();
41
+ uint256 value = buckets .valueOf[id ];
42
+ buckets .valueOf[id ] = newValue ;
39
43
40
44
if (value == 0 ) {
41
- if (_newValue == 0 ) revert ZeroValue ();
42
- _insert (_buckets, _id, computeBucket (_newValue), _head);
45
+ if (newValue == 0 ) revert ZeroValue ();
46
+ // `highestSetBit` is used to compute the bucket associated with `newValue`.
47
+ _insert (buckets, id, highestSetBit (newValue), head);
43
48
return ;
44
49
}
45
50
46
- uint256 currentBucket = computeBucket (value);
47
- if (_newValue == 0 ) {
48
- _remove (_buckets, _id, currentBucket);
51
+ // `highestSetBit` is used to compute the bucket associated with `value`.
52
+ uint256 currentBucket = highestSetBit (value);
53
+ if (newValue == 0 ) {
54
+ _remove (buckets, id, currentBucket);
49
55
return ;
50
56
}
51
57
52
- uint256 newBucket = computeBucket (_newValue);
58
+ // `highestSetBit` is used to compute the bucket associated with `newValue`.
59
+ uint256 newBucket = highestSetBit (newValue);
53
60
if (newBucket != currentBucket) {
54
- _remove (_buckets, _id , currentBucket);
55
- _insert (_buckets, _id , newBucket, _head );
61
+ _remove (buckets, id , currentBucket);
62
+ _insert (buckets, id , newBucket, head );
56
63
}
57
64
}
58
65
59
- /// @notice Returns the address in `_buckets ` that is a candidate for matching the value `_value `.
60
- /// @param _buckets The buckets to get the head.
61
- /// @param _value The value to match.
66
+ /// @notice Returns the address in `buckets ` that is a candidate for matching the value `value `.
67
+ /// @param buckets The buckets to get the head.
68
+ /// @param value The value to match.
62
69
/// @return The address of the head.
63
- function getMatch (Buckets storage _buckets , uint256 _value ) internal view returns (address ) {
64
- uint256 bucketsMask = _buckets .bucketsMask;
70
+ function getMatch (Buckets storage buckets , uint256 value ) internal view returns (address ) {
71
+ uint256 bucketsMask = buckets .bucketsMask;
65
72
if (bucketsMask == 0 ) return address (0 );
66
- uint256 lowerMask = setLowerBits (_value);
67
73
68
- uint256 next = nextBucket (lowerMask, bucketsMask);
74
+ uint256 next = nextBucket (value, bucketsMask);
75
+ if (next != 0 ) return buckets.buckets[next].getNext (address (0 ));
69
76
70
- if (next != 0 ) return _buckets.buckets[next].getHead ();
71
-
72
- uint256 prev = prevBucket (lowerMask, bucketsMask);
73
-
74
- return _buckets.buckets[prev].getHead ();
77
+ // `highestSetBit` is used to compute the highest non-empty bucket.
78
+ // Knowing that `next` == 0, it is also the highest previous non-empty bucket.
79
+ uint256 prev = highestSetBit (bucketsMask);
80
+ return buckets.buckets[prev].getNext (address (0 ));
75
81
}
76
82
77
- /// PRIVATE // /
83
+ /* PRIVATE * /
78
84
79
- /// @notice Removes an account in the `_buckets `.
85
+ /// @notice Removes an account in the `buckets `.
80
86
/// @dev Does not update the value.
81
- /// @param _buckets The buckets to modify.
82
- /// @param _id The address of the account to remove.
83
- /// @param _bucket The mask of the bucket where to remove.
87
+ /// @param buckets The buckets to modify.
88
+ /// @param id The address of the account to remove.
89
+ /// @param bucket The mask of the bucket where to remove.
84
90
function _remove (
85
- Buckets storage _buckets ,
86
- address _id ,
87
- uint256 _bucket
91
+ Buckets storage buckets ,
92
+ address id ,
93
+ uint256 bucket
88
94
) private {
89
- if (_buckets .buckets[_bucket ].remove (_id )) _buckets .bucketsMask &= ~ _bucket ;
95
+ if (buckets .buckets[bucket ].remove (id )) buckets .bucketsMask &= ~ bucket ;
90
96
}
91
97
92
- /// @notice Inserts an account in the `_buckets `.
93
- /// @dev Expects that `_id ` != 0.
98
+ /// @notice Inserts an account in the `buckets `.
99
+ /// @dev Expects that `id ` != 0.
94
100
/// @dev Does not update the value.
95
- /// @param _buckets The buckets to modify.
96
- /// @param _id The address of the account to update.
97
- /// @param _bucket The mask of the bucket where to insert.
98
- /// @param _head Whether to insert at the head or at the tail of the list.
101
+ /// @param buckets The buckets to modify.
102
+ /// @param id The address of the account to update.
103
+ /// @param bucket The mask of the bucket where to insert.
104
+ /// @param head Whether to insert at the head or at the tail of the list.
99
105
function _insert (
100
- Buckets storage _buckets ,
101
- address _id ,
102
- uint256 _bucket ,
103
- bool _head
106
+ Buckets storage buckets ,
107
+ address id ,
108
+ uint256 bucket ,
109
+ bool head
104
110
) private {
105
- if (_buckets .buckets[_bucket ].insert (_id, _head )) _buckets .bucketsMask |= _bucket ;
111
+ if (buckets .buckets[bucket ].insert (id, head )) buckets .bucketsMask |= bucket ;
106
112
}
107
113
108
- /// PURE HELPERS // /
114
+ /* PURE HELPERS * /
109
115
110
- /// @notice Returns the bucket in which the given value would fall.
111
- function computeBucket (uint256 _value ) internal pure returns (uint256 ) {
112
- uint256 lowerMask = setLowerBits (_value);
116
+ /// @notice Returns the highest set bit.
117
+ /// @dev Used to compute the bucket associated to a given `value`.
118
+ /// @dev Used to compute the highest non empty bucket given the `bucketsMask`.
119
+ function highestSetBit (uint256 value ) internal pure returns (uint256 ) {
120
+ uint256 lowerMask = setLowerBits (value);
113
121
return lowerMask ^ (lowerMask >> 1 );
114
122
}
115
123
@@ -128,23 +136,13 @@ library LogarithmicBuckets {
128
136
}
129
137
}
130
138
131
- /// @notice Returns the following bucket which contains greater values.
139
+ /// @notice Returns the lowest non-empty bucket containing larger values.
132
140
/// @dev The bucket returned is the lowest that is in `bucketsMask` and not in `lowerMask`.
133
- function nextBucket (uint256 lowerMask , uint256 bucketsMask )
134
- internal
135
- pure
136
- returns (uint256 bucket )
137
- {
141
+ function nextBucket (uint256 value , uint256 bucketsMask ) internal pure returns (uint256 bucket ) {
142
+ uint256 lowerMask = setLowerBits (value);
138
143
assembly {
139
144
let higherBucketsMask := and (not (lowerMask), bucketsMask)
140
145
bucket := and (higherBucketsMask, add (not (higherBucketsMask), 1 ))
141
146
}
142
147
}
143
-
144
- /// @notice Returns the preceding bucket which contains smaller values.
145
- /// @dev The bucket returned is the highest that is in both `bucketsMask` and `lowerMask`.
146
- function prevBucket (uint256 lowerMask , uint256 bucketsMask ) internal pure returns (uint256 ) {
147
- uint256 lowerBucketsMask = setLowerBits (lowerMask & bucketsMask);
148
- return lowerBucketsMask ^ (lowerBucketsMask >> 1 );
149
- }
150
148
}
0 commit comments