Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d78ae17
Migrate 'arrays'
RenanSouza2 Nov 12, 2023
52b3bd8
fix findUpperBound and add findLowerBound
Amxx Nov 17, 2023
9a1411b
add memory variants
Amxx Nov 20, 2023
c4726c8
Merge branch 'master' into feature/array-bound-with-duplicates
Amxx Jan 18, 2024
a6ec616
fix merge
Amxx Jan 18, 2024
23e8db9
fix lint
Amxx Jan 18, 2024
c72591f
minimize change
Amxx Jan 18, 2024
9162e42
add changeset
Amxx Jan 18, 2024
ed1de5b
Apply suggestions from code review
Amxx Jan 18, 2024
4c1c7f4
add Arrays.sort
Amxx Jan 18, 2024
abd07b6
add sort test
Amxx Jan 18, 2024
1e89815
fix lint
Amxx Jan 18, 2024
b73989d
codespell
Amxx Jan 19, 2024
79bf367
add fuzzing tests for Arrays.sort
Amxx Jan 19, 2024
f2d49ef
add unsafeMemoryAccess tests
Amxx Jan 22, 2024
c043453
Merge branch 'master' into feature/quicksort
Amxx Jan 29, 2024
c75de32
fix lint
Amxx Jan 29, 2024
f823bee
lint
Amxx Jan 30, 2024
c90f12b
Update contracts/utils/Arrays.sol
Amxx Feb 2, 2024
180a969
Update contracts/utils/Arrays.sol
Amxx Feb 2, 2024
708972f
Apply suggestions from code review
Amxx Feb 2, 2024
533e6cd
Merge branch 'master' into feature/quicksort
Amxx Feb 2, 2024
3f1f0a5
Update contracts/utils/Arrays.sol
ernestognw Feb 2, 2024
5a0ad7f
Add comments to `_quickSort`
ernestognw Feb 3, 2024
a8e6f54
Lint
ernestognw Feb 3, 2024
7600291
Update contracts/utils/Arrays.sol
Amxx Feb 5, 2024
6f163d2
Update contracts/utils/Arrays.sol
Amxx Feb 5, 2024
8983066
cache the pivot and improve doc
Amxx Feb 5, 2024
8704763
Apply suggestions from code review
Amxx Feb 5, 2024
4336e2e
Merge branch 'master' into feature/quicksort
Amxx Feb 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add unsafeMemoryAccess tests
  • Loading branch information
Amxx committed Jan 22, 2024
commit f2d49ef839f1c0a149ba81b49d3f3b99274a712a
15 changes: 13 additions & 2 deletions contracts/utils/Arrays.sol
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ library Arrays {
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
Expand All @@ -309,7 +309,18 @@ library Arrays {
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}

/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
Expand Down
70 changes: 43 additions & 27 deletions test/utils/Arrays.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ const compareNumbers = (a, b) => (a > b ? 1 : a < b ? -1 : 0);
const hasDuplicates = array => array.some((v, i) => array.indexOf(v) != i);

describe('Arrays', function () {
describe('sort', function () {
const fixture = async () => {
return { mock: await ethers.deployContract('$Arrays') };
};
const fixture = async () => {
return { mock: await ethers.deployContract('$Arrays') };
};

beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});

describe('sort', function () {
for (const length of [0, 1, 2, 8, 32, 128]) {
it(`sort array of length ${length}`, async function () {
this.elements = randomArray(generators.uint256, length);
Expand Down Expand Up @@ -121,7 +121,7 @@ describe('Arrays', function () {
})) {
describe(title, function () {
const fixture = async () => {
return { mock: await ethers.deployContract('Uint256ArraysMock', [array]) };
return { instance: await ethers.deployContract('Uint256ArraysMock', [array]) };
};

beforeEach(async function () {
Expand All @@ -133,17 +133,17 @@ describe('Arrays', function () {
it('[deprecated] findUpperBound', async function () {
// findUpperBound does not support duplicated
if (hasDuplicates(array)) this.skip();
expect(await this.mock.findUpperBound(input)).to.be.equal(lowerBound(array, input));
expect(await this.instance.findUpperBound(input)).to.be.equal(lowerBound(array, input));
});

it('lowerBound', async function () {
expect(await this.mock.lowerBound(input)).to.be.equal(lowerBound(array, input));
expect(await this.mock.lowerBoundMemory(array, input)).to.be.equal(lowerBound(array, input));
expect(await this.instance.lowerBound(input)).to.be.equal(lowerBound(array, input));
expect(await this.instance.lowerBoundMemory(array, input)).to.be.equal(lowerBound(array, input));
});

it('upperBound', async function () {
expect(await this.mock.upperBound(input)).to.be.equal(upperBound(array, input));
expect(await this.mock.upperBoundMemory(array, input)).to.be.equal(upperBound(array, input));
expect(await this.instance.upperBound(input)).to.be.equal(upperBound(array, input));
expect(await this.instance.upperBoundMemory(array, input)).to.be.equal(upperBound(array, input));
});
});
}
Expand All @@ -152,28 +152,44 @@ describe('Arrays', function () {
});

describe('unsafeAccess', function () {
for (const [title, { artifact, elements }] of Object.entries({
for (const [type, { artifact, elements }] of Object.entries({
address: { artifact: 'AddressArraysMock', elements: randomArray(generators.address, 10) },
bytes32: { artifact: 'Bytes32ArraysMock', elements: randomArray(generators.bytes32, 10) },
uint256: { artifact: 'Uint256ArraysMock', elements: randomArray(generators.uint256, 10) },
})) {
describe(title, function () {
const fixture = async () => {
return { mock: await ethers.deployContract(artifact, [elements]) };
};
describe(type, function () {
describe('storage', function () {
const fixture = async () => {
return { instance: await ethers.deployContract(artifact, [elements]) };
};

beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});

beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
for (const i in elements) {
it(`unsafeAccess within bounds #${i}`, async function () {
expect(await this.instance.unsafeAccess(i)).to.equal(elements[i]);
});
}

for (const i in elements) {
it(`unsafeAccess within bounds #${i}`, async function () {
expect(await this.mock.unsafeAccess(i)).to.equal(elements[i]);
it('unsafeAccess outside bounds', async function () {
await expect(this.instance.unsafeAccess(elements.length)).to.not.be.rejected;
});
}
});

it('unsafeAccess outside bounds', async function () {
await expect(this.mock.unsafeAccess(elements.length)).to.not.be.rejected;
describe('memory', function () {
const fragment = `$unsafeMemoryAccess(${type}[] arr, uint256 pos)`;

for (const i in elements) {
it(`unsafeMemoryAccess within bounds #${i}`, async function () {
expect(await this.mock[fragment](elements, i)).to.equal(elements[i]);
});
}

it('unsafeMemoryAccess outside bounds', async function () {
await expect(this.mock[fragment](elements, elements.length)).to.not.be.rejected;
});
});
});
}
Expand Down