Skip to content
This repository was archived by the owner on Dec 17, 2025. It is now read-only.

Commit 25de5ba

Browse files
authored
Merge pull request #27 from morpho-labs/refactor/naming-and-getters
Refactor/naming and getters
2 parents d76496a + b268250 commit 25de5ba

5 files changed

Lines changed: 91 additions & 109 deletions

src/LogarithmicBuckets.sol

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import "./BucketDLL.sol";
66
library LogarithmicBuckets {
77
using BucketDLL for BucketDLL.List;
88

9-
struct BucketList {
10-
mapping(uint256 => BucketDLL.List) lists;
9+
struct Buckets {
10+
mapping(uint256 => BucketDLL.List) buckets;
1111
mapping(address => uint256) valueOf;
1212
uint256 bucketsMask;
1313
}
@@ -28,7 +28,7 @@ library LogarithmicBuckets {
2828
/// @param _newValue The new value of the account.
2929
/// @param _head Indicates whether to insert the new values at the head or at the tail of the buckets list.
3030
function update(
31-
BucketList storage _buckets,
31+
Buckets storage _buckets,
3232
address _id,
3333
uint256 _newValue,
3434
bool _head
@@ -56,41 +56,22 @@ library LogarithmicBuckets {
5656
}
5757
}
5858

59-
/// @notice Returns the value of `_id`.
60-
/// @param _buckets The buckets to search in.
61-
/// @param _id The address of the account.
62-
function getValueOf(BucketList storage _buckets, address _id) internal view returns (uint256) {
63-
return _buckets.valueOf[_id];
64-
}
65-
66-
/// @notice Returns the bucket in which to look for `_value`.
67-
/// @param _buckets The buckets to search in.
68-
/// @param _value The value to look for.
69-
function getBucketOf(BucketList storage _buckets, uint256 _value)
70-
internal
71-
view
72-
returns (BucketDLL.List storage)
73-
{
74-
uint256 bucket = computeBucket(_value);
75-
return _buckets.lists[bucket];
76-
}
77-
7859
/// @notice Returns the address in `_buckets` that is a candidate for matching the value `_value`.
7960
/// @param _buckets The buckets to get the head.
8061
/// @param _value The value to match.
8162
/// @return The address of the head.
82-
function getMatch(BucketList storage _buckets, uint256 _value) internal view returns (address) {
63+
function getMatch(Buckets storage _buckets, uint256 _value) internal view returns (address) {
8364
uint256 bucketsMask = _buckets.bucketsMask;
8465
if (bucketsMask == 0) return address(0);
8566
uint256 lowerMask = setLowerBits(_value);
8667

8768
uint256 next = nextBucket(lowerMask, bucketsMask);
8869

89-
if (next != 0) return _buckets.lists[next].getHead();
70+
if (next != 0) return _buckets.buckets[next].getHead();
9071

9172
uint256 prev = prevBucket(lowerMask, bucketsMask);
9273

93-
return _buckets.lists[prev].getHead();
74+
return _buckets.buckets[prev].getHead();
9475
}
9576

9677
/// PRIVATE ///
@@ -101,11 +82,11 @@ library LogarithmicBuckets {
10182
/// @param _id The address of the account to remove.
10283
/// @param _bucket The mask of the bucket where to remove.
10384
function _remove(
104-
BucketList storage _buckets,
85+
Buckets storage _buckets,
10586
address _id,
10687
uint256 _bucket
10788
) private {
108-
if (_buckets.lists[_bucket].remove(_id))
89+
if (_buckets.buckets[_bucket].remove(_id))
10990
_buckets.bucketsMask &= _bucket ^ type(uint256).max;
11091
}
11192

@@ -117,14 +98,16 @@ library LogarithmicBuckets {
11798
/// @param _bucket The mask of the bucket where to insert.
11899
/// @param _head Whether to insert at the head or at the tail of the list.
119100
function _insert(
120-
BucketList storage _buckets,
101+
Buckets storage _buckets,
121102
address _id,
122103
uint256 _bucket,
123104
bool _head
124105
) private {
125-
if (_buckets.lists[_bucket].insert(_id, _head)) _buckets.bucketsMask |= _bucket;
106+
if (_buckets.buckets[_bucket].insert(_id, _head)) _buckets.bucketsMask |= _bucket;
126107
}
127108

109+
/// PURE HELPERS ///
110+
128111
/// @notice Returns the bucket in which the given value would fall.
129112
function computeBucket(uint256 _value) internal pure returns (uint256) {
130113
uint256 lowerMask = setLowerBits(_value);

test/TestLogarithmicBuckets.t.sol

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import "./mocks/LogarithmicBucketsMock.sol";
66

77
contract TestLogarithmicBuckets is LogarithmicBucketsMock, Test {
88
using BucketDLL for BucketDLL.List;
9-
using LogarithmicBuckets for LogarithmicBuckets.BucketList;
9+
using LogarithmicBuckets for LogarithmicBuckets.Buckets;
1010

1111
uint256 public accountsLength = 50;
1212
address[] public accounts;
@@ -21,30 +21,30 @@ contract TestLogarithmicBuckets is LogarithmicBucketsMock, Test {
2121
}
2222

2323
function testInsertOneSingleAccount(bool _head) public {
24-
bucketList.update(accounts[0], 3, _head);
24+
buckets.update(accounts[0], 3, _head);
2525

26-
assertEq(bucketList.getValueOf(accounts[0]), 3);
27-
assertEq(bucketList.getMatch(0), accounts[0]);
28-
assertEq(bucketList.getBucketOf(3).getHead(), accounts[0]);
29-
assertEq(bucketList.getBucketOf(2).getHead(), accounts[0]);
26+
assertEq(buckets.valueOf[accounts[0]], 3);
27+
assertEq(buckets.getMatch(0), accounts[0]);
28+
assertEq(buckets.buckets[2].getHead(), accounts[0]);
29+
assertEq(buckets.buckets[2].getHead(), accounts[0]);
3030
}
3131

3232
function testUpdatingFromZeroToZeroShouldRevert(bool _head) public {
3333
vm.expectRevert(abi.encodeWithSignature("ZeroValue()"));
34-
bucketList.update(accounts[0], 0, _head);
34+
buckets.update(accounts[0], 0, _head);
3535
}
3636

3737
function testShouldNotInsertZeroAddress(bool _head) public {
3838
vm.expectRevert(abi.encodeWithSignature("ZeroAddress()"));
39-
bucketList.update(address(0), 10, _head);
39+
buckets.update(address(0), 10, _head);
4040
}
4141

4242
function testShouldHaveTheRightOrderWithinABucketFIFO() public {
43-
bucketList.update(accounts[0], 16, false);
44-
bucketList.update(accounts[1], 16, false);
45-
bucketList.update(accounts[2], 16, false);
43+
buckets.update(accounts[0], 16, false);
44+
buckets.update(accounts[1], 16, false);
45+
buckets.update(accounts[2], 16, false);
4646

47-
BucketDLL.List storage list = bucketList.getBucketOf(16);
47+
BucketDLL.List storage list = buckets.buckets[16];
4848
address head = list.getNext(address(0));
4949
address next1 = list.getNext(head);
5050
address next2 = list.getNext(next1);
@@ -54,11 +54,11 @@ contract TestLogarithmicBuckets is LogarithmicBucketsMock, Test {
5454
}
5555

5656
function testShouldHaveTheRightOrderWithinABucketLIFO() public {
57-
bucketList.update(accounts[0], 16, true);
58-
bucketList.update(accounts[1], 16, true);
59-
bucketList.update(accounts[2], 16, true);
57+
buckets.update(accounts[0], 16, true);
58+
buckets.update(accounts[1], 16, true);
59+
buckets.update(accounts[2], 16, true);
6060

61-
BucketDLL.List storage list = bucketList.getBucketOf(16);
61+
BucketDLL.List storage list = buckets.buckets[16];
6262
address head = list.getNext(address(0));
6363
address next1 = list.getNext(head);
6464
address next2 = list.getNext(next1);
@@ -68,52 +68,52 @@ contract TestLogarithmicBuckets is LogarithmicBucketsMock, Test {
6868
}
6969

7070
function testInsertRemoveOneSingleAccount(bool _head1, bool _head2) public {
71-
bucketList.update(accounts[0], 1, _head1);
72-
bucketList.update(accounts[0], 0, _head2);
71+
buckets.update(accounts[0], 1, _head1);
72+
buckets.update(accounts[0], 0, _head2);
7373

74-
assertEq(bucketList.getValueOf(accounts[0]), 0);
75-
assertEq(bucketList.getMatch(0), address(0));
76-
assertEq(bucketList.getBucketOf(1).getHead(), address(0));
74+
assertEq(buckets.valueOf[accounts[0]], 0);
75+
assertEq(buckets.getMatch(0), address(0));
76+
assertEq(buckets.buckets[1].getHead(), address(0));
7777
}
7878

7979
function testShouldInsertTwoAccounts(bool _head1, bool _head2) public {
80-
bucketList.update(accounts[0], 16, _head1);
81-
bucketList.update(accounts[1], 4, _head2);
80+
buckets.update(accounts[0], 16, _head1);
81+
buckets.update(accounts[1], 4, _head2);
8282

83-
assertEq(bucketList.getMatch(16), accounts[0]);
84-
assertEq(bucketList.getMatch(2), accounts[1]);
85-
assertEq(bucketList.getBucketOf(4).getHead(), accounts[1]);
83+
assertEq(buckets.getMatch(16), accounts[0]);
84+
assertEq(buckets.getMatch(2), accounts[1]);
85+
assertEq(buckets.buckets[4].getHead(), accounts[1]);
8686
}
8787

8888
function testShouldRemoveOneAccountOverTwo() public {
89-
bucketList.update(accounts[0], 4, false);
90-
bucketList.update(accounts[1], 16, false);
91-
bucketList.update(accounts[0], 0, false);
92-
93-
assertEq(bucketList.getMatch(4), accounts[1]);
94-
assertEq(bucketList.getValueOf(accounts[0]), 0);
95-
assertEq(bucketList.getValueOf(accounts[1]), 16);
96-
assertEq(bucketList.getBucketOf(16).getHead(), accounts[1]);
97-
assertEq(bucketList.getBucketOf(4).getHead(), address(0));
89+
buckets.update(accounts[0], 4, false);
90+
buckets.update(accounts[1], 16, false);
91+
buckets.update(accounts[0], 0, false);
92+
93+
assertEq(buckets.getMatch(4), accounts[1]);
94+
assertEq(buckets.valueOf[accounts[0]], 0);
95+
assertEq(buckets.valueOf[accounts[1]], 16);
96+
assertEq(buckets.buckets[16].getHead(), accounts[1]);
97+
assertEq(buckets.buckets[4].getHead(), address(0));
9898
}
9999

100100
function testShouldRemoveBothAccounts() public {
101-
bucketList.update(accounts[0], 4, true);
102-
bucketList.update(accounts[1], 4, true);
103-
bucketList.update(accounts[0], 0, true);
104-
bucketList.update(accounts[1], 0, true);
101+
buckets.update(accounts[0], 4, true);
102+
buckets.update(accounts[1], 4, true);
103+
buckets.update(accounts[0], 0, true);
104+
buckets.update(accounts[1], 0, true);
105105

106-
assertEq(bucketList.getMatch(4), address(0));
106+
assertEq(buckets.getMatch(4), address(0));
107107
}
108108

109109
function testGetMatch() public {
110-
assertEq(bucketList.getMatch(0), address(0));
111-
assertEq(bucketList.getMatch(1000), address(0));
110+
assertEq(buckets.getMatch(0), address(0));
111+
assertEq(buckets.getMatch(1000), address(0));
112112

113-
bucketList.update(accounts[0], 16, false);
114-
assertEq(bucketList.getMatch(1), accounts[0], "head before");
115-
assertEq(bucketList.getMatch(16), accounts[0], "head equal");
116-
assertEq(bucketList.getMatch(32), accounts[0], "head above");
113+
buckets.update(accounts[0], 16, false);
114+
assertEq(buckets.getMatch(1), accounts[0], "head before");
115+
assertEq(buckets.getMatch(16), accounts[0], "head equal");
116+
assertEq(buckets.getMatch(32), accounts[0], "head above");
117117
}
118118

119119
function isPowerOfTwo(uint256 x) public pure returns (bool) {
@@ -134,8 +134,8 @@ contract TestLogarithmicBuckets is LogarithmicBucketsMock, Test {
134134

135135
function testProveNextBucket(uint256 _value) public {
136136
uint256 curr = LogarithmicBuckets.computeBucket(_value);
137-
uint256 next = nextBucket(_value);
138-
uint256 bucketsMask = bucketList.bucketsMask;
137+
uint256 next = nextBucketValue(_value);
138+
uint256 bucketsMask = buckets.bucketsMask;
139139
// check that `next` is a strictly higer non-empty bucket, or zero
140140
assertTrue(next == 0 || isPowerOfTwo(next));
141141
assertTrue(next == 0 || next > curr);
@@ -151,8 +151,8 @@ contract TestLogarithmicBuckets is LogarithmicBucketsMock, Test {
151151

152152
function testProvePrevBucket(uint256 _value) public {
153153
uint256 curr = LogarithmicBuckets.computeBucket(_value);
154-
uint256 prev = prevBucket(_value);
155-
uint256 bucketsMask = bucketList.bucketsMask;
154+
uint256 prev = prevBucketValue(_value);
155+
uint256 bucketsMask = buckets.bucketsMask;
156156
// check that `prev` is a non-empty bucket that is lower than or equal to `curr`; or zero
157157
assertTrue(prev == 0 || isPowerOfTwo(prev));
158158
assertTrue(prev <= curr);

test/TestLogarithmicBucketsGas.t.sol

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import "forge-std/Test.sol";
55
import "src/LogarithmicBuckets.sol";
66

77
contract TestLogarithmicBucketsGas is Test {
8-
using LogarithmicBuckets for LogarithmicBuckets.BucketList;
8+
using LogarithmicBuckets for LogarithmicBuckets.Buckets;
99

10-
LogarithmicBuckets.BucketList internal bucketList;
10+
LogarithmicBuckets.Buckets internal buckets;
1111

1212
// Gas accounting.
1313
uint256 internal insertCost;
@@ -33,19 +33,19 @@ contract TestLogarithmicBucketsGas is Test {
3333
if (amount % 4 < 2) {
3434
// Measure insert.
3535
startGasMetering();
36-
bucketList.update(address(uint160(amount)), amount, true);
36+
buckets.update(address(uint160(amount)), amount, true);
3737
insertCost += stopGasMetering();
3838
insertCount++;
3939
}
4040
// Update value in same bucket (p=1/4).
4141
else if (amount % 4 == 2) {
4242
// Get an account to update its value.
43-
address toUpdate = bucketList.getMatch(amount);
43+
address toUpdate = buckets.getMatch(amount);
4444

4545
if (toUpdate != address(0)) {
4646
// Measure updateValue.
4747
startGasMetering();
48-
bucketList.update(toUpdate, amount, true);
48+
buckets.update(toUpdate, amount, true);
4949
updateValueCost += stopGasMetering();
5050
updateValueCount++;
5151
}
@@ -54,14 +54,14 @@ contract TestLogarithmicBucketsGas is Test {
5454
else {
5555
// Measure getMatch.
5656
startGasMetering();
57-
address toUpdate = bucketList.getMatch(amount);
57+
address toUpdate = buckets.getMatch(amount);
5858
getMatchCost += stopGasMetering();
5959
getMatchCount++;
6060

6161
if (toUpdate != address(0)) {
6262
// Measure remove.
6363
startGasMetering();
64-
bucketList.update(bucketList.getMatch(amount), 0, true);
64+
buckets.update(buckets.getMatch(amount), 0, true);
6565
removeCost += stopGasMetering();
6666
removeCount++;
6767
}

test/TestLogarithmicBucketsInvariant.t.sol

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ contract TestLogarithmicBucketsInvariant is Test, Random {
3232

3333
contract LogarithmicBucketsSeenMock is LogarithmicBucketsMock {
3434
using BucketDLL for BucketDLL.List;
35-
using LogarithmicBuckets for LogarithmicBuckets.BucketList;
35+
using LogarithmicBuckets for LogarithmicBuckets.Buckets;
3636

3737
address[] public seen;
3838
mapping(address => bool) public isSeen;
@@ -54,14 +54,12 @@ contract LogarithmicBucketsSeenMock is LogarithmicBucketsMock {
5454
super.update(_id, _newValue, _head);
5555
}
5656

57-
function getPrev(uint256 _value, address _id) public view returns (address) {
58-
BucketDLL.List storage bucket = bucketList.getBucketOf(_value);
59-
return bucket.getPrev(_id);
57+
function getPrev(uint256 _bucket, address _id) public view returns (address) {
58+
return buckets.buckets[_bucket].getPrev(_id);
6059
}
6160

62-
function getNext(uint256 _value, address _id) public view returns (address) {
63-
BucketDLL.List storage bucket = bucketList.getBucketOf(_value);
64-
return bucket.getNext(_id);
61+
function getNext(uint256 _bucket, address _id) public view returns (address) {
62+
return buckets.buckets[_bucket].getNext(_id);
6563
}
6664
}
6765

@@ -77,10 +75,11 @@ contract TestLogarithmicBucketsSeenInvariant is Test, Random {
7775
address user = buckets.seen(i);
7876
uint256 value = buckets.getValueOf(user);
7977
if (value != 0) {
80-
address next = buckets.getNext(value, user);
81-
address prev = buckets.getPrev(value, user);
82-
assertEq(buckets.getNext(value, prev), user);
83-
assertEq(buckets.getPrev(value, next), user);
78+
uint256 bucket = LogarithmicBuckets.computeBucket(value);
79+
address next = buckets.getNext(bucket, user);
80+
address prev = buckets.getPrev(bucket, user);
81+
assertEq(buckets.getNext(bucket, prev), user);
82+
assertEq(buckets.getPrev(bucket, next), user);
8483
}
8584
}
8685
}

0 commit comments

Comments
 (0)