@@ -3,6 +3,10 @@ pragma solidity ^0.8.0;
33
44import "./BucketDLL.sol " ;
55
6+ /// @title LogarithmicBuckets
7+ /// @author Morpho Labs
8+ /// @custom:contact security@morpho.xyz
9+ /// @notice The logarithmic buckets data-structure.
610library LogarithmicBuckets {
711 using BucketDLL for BucketDLL.List;
812
@@ -12,104 +16,108 @@ library LogarithmicBuckets {
1216 uint256 bucketsMask;
1317 }
1418
15- /// ERRORS // /
19+ /* ERRORS * /
1620
1721 /// @notice Thrown when the address is zero at insertion.
1822 error ZeroAddress ();
1923
2024 /// @notice Thrown when 0 value is inserted.
2125 error ZeroValue ();
2226
23- /// INTERNAL // /
27+ /* INTERNAL * /
2428
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.
3034 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
3539 ) 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 ;
3943
4044 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);
4348 return ;
4449 }
4550
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);
4955 return ;
5056 }
5157
52- uint256 newBucket = computeBucket (_newValue);
58+ // `highestSetBit` is used to compute the bucket associated with `newValue`.
59+ uint256 newBucket = highestSetBit (newValue);
5360 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 );
5663 }
5764 }
5865
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.
6269 /// @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;
6572 if (bucketsMask == 0 ) return address (0 );
66- uint256 lowerMask = setLowerBits (_value);
6773
68- uint256 next = nextBucket (lowerMask, bucketsMask);
74+ uint256 next = nextBucket (value, bucketsMask);
75+ if (next != 0 ) return buckets.buckets[next].getNext (address (0 ));
6976
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 ));
7581 }
7682
77- /// PRIVATE // /
83+ /* PRIVATE * /
7884
79- /// @notice Removes an account in the `_buckets `.
85+ /// @notice Removes an account in the `buckets `.
8086 /// @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.
8490 function _remove (
85- Buckets storage _buckets ,
86- address _id ,
87- uint256 _bucket
91+ Buckets storage buckets ,
92+ address id ,
93+ uint256 bucket
8894 ) private {
89- if (_buckets .buckets[_bucket ].remove (_id )) _buckets .bucketsMask &= ~ _bucket ;
95+ if (buckets .buckets[bucket ].remove (id )) buckets .bucketsMask &= ~ bucket ;
9096 }
9197
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.
94100 /// @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.
99105 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
104110 ) private {
105- if (_buckets .buckets[_bucket ].insert (_id, _head )) _buckets .bucketsMask |= _bucket ;
111+ if (buckets .buckets[bucket ].insert (id, head )) buckets .bucketsMask |= bucket ;
106112 }
107113
108- /// PURE HELPERS // /
114+ /* PURE HELPERS * /
109115
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);
113121 return lowerMask ^ (lowerMask >> 1 );
114122 }
115123
@@ -128,23 +136,13 @@ library LogarithmicBuckets {
128136 }
129137 }
130138
131- /// @notice Returns the following bucket which contains greater values.
139+ /// @notice Returns the lowest non-empty bucket containing larger values.
132140 /// @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);
138143 assembly {
139144 let higherBucketsMask := and (not (lowerMask), bucketsMask)
140145 bucket := and (higherBucketsMask, add (not (higherBucketsMask), 1 ))
141146 }
142147 }
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- }
150148}
0 commit comments