Skip to content
Merged
Changes from all commits
Commits
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
146 changes: 140 additions & 6 deletions test/LibCast.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,156 @@ import {LibCast} from "../src/LibCast.sol";

contract LibCastTest is Test {
function testAddressesArrayRound0(uint256[] memory us) public pure {
assertEq(us, LibCast.asUint256Array(LibCast.asAddressesArray(us)));
// The input and the round trip are the same buffer, so comparing them
// against each other cannot observe a cast that rewrites elements where
// they lie. The expected words are snapshotted into an independent
// buffer first so that such a rewrite is observable.
uint256[] memory expected = new uint256[](us.length);
for (uint256 i = 0; i < us.length; i++) {
expected[i] = us[i];
}

address[] memory intermediate = LibCast.asAddressesArray(us);

// The intermediate array is what a consumer of the cast reads, and a
// cast leaves every bit alone, including the bits that do not form part
// of a valid `address`.
assertEq(intermediate.length, expected.length);
for (uint256 i = 0; i < expected.length; i++) {
uint256 word;
assembly ("memory-safe") {
word := mload(add(add(intermediate, 0x20), mul(i, 0x20)))
}
assertEq(word, expected[i]);
}

assertEq(expected, LibCast.asUint256Array(intermediate));
}

function testAddressesArrayRound1(address[] memory addresses) public pure {
assertEq(addresses, LibCast.asAddressesArray(LibCast.asUint256Array(addresses)));
address[] memory expected = new address[](addresses.length);
for (uint256 i = 0; i < addresses.length; i++) {
expected[i] = addresses[i];
}

uint256[] memory intermediate = LibCast.asUint256Array(addresses);

assertEq(intermediate.length, expected.length);
for (uint256 i = 0; i < expected.length; i++) {
assertEq(intermediate[i], uint256(uint160(expected[i])));
}

assertEq(expected, LibCast.asAddressesArray(intermediate));
}

function testBytes32ArrayRound0(uint256[] memory us) public pure {
assertEq(us, LibCast.asUint256Array(LibCast.asBytes32Array(us)));
uint256[] memory expected = new uint256[](us.length);
for (uint256 i = 0; i < us.length; i++) {
expected[i] = us[i];
}

bytes32[] memory intermediate = LibCast.asBytes32Array(us);

assertEq(intermediate.length, expected.length);
for (uint256 i = 0; i < expected.length; i++) {
assertEq(uint256(intermediate[i]), expected[i]);
}

assertEq(expected, LibCast.asUint256Array(intermediate));
}

function testBytes32ArrayRound1(bytes32[] memory b32s) public pure {
bytes32[] memory round_ = LibCast.asBytes32Array(LibCast.asUint256Array(b32s));

bytes32[] memory expected = new bytes32[](b32s.length);
for (uint256 i = 0; i < b32s.length; i++) {
assertEq(b32s[i], round_[i]);
expected[i] = b32s[i];
}

uint256[] memory intermediate = LibCast.asUint256Array(b32s);

assertEq(intermediate.length, expected.length);
for (uint256 i = 0; i < expected.length; i++) {
assertEq(intermediate[i], uint256(expected[i]));
}

bytes32[] memory round_ = LibCast.asBytes32Array(intermediate);

assertEq(round_.length, expected.length);
for (uint256 i = 0; i < expected.length; i++) {
assertEq(expected[i], round_[i]);
}
}

/// A cast retypes the data where it lies, so the returned array is the same
/// buffer as the input rather than a copy of it. Callers rely on this: the
/// two names address one mutable structure.
function testAsAddressesArrayRetypesInPlace(uint256[] memory us) public pure {
uint256 usPointer;
assembly ("memory-safe") {
usPointer := us
}

address[] memory addresses = LibCast.asAddressesArray(us);

uint256 addressesPointer;
assembly ("memory-safe") {
addressesPointer := addresses
}
assertEq(addressesPointer, usPointer);
}

function testAsUint256ArrayFromAddressesRetypesInPlace(address[] memory addresses) public pure {
uint256 addressesPointer;
assembly ("memory-safe") {
addressesPointer := addresses
}

uint256[] memory us = LibCast.asUint256Array(addresses);

uint256 usPointer;
assembly ("memory-safe") {
usPointer := us
}
assertEq(usPointer, addressesPointer);
}

function testAsBytes32ArrayRetypesInPlace(uint256[] memory us) public pure {
uint256 usPointer;
assembly ("memory-safe") {
usPointer := us
}

bytes32[] memory b32s = LibCast.asBytes32Array(us);

uint256 b32sPointer;
assembly ("memory-safe") {
b32sPointer := b32s
}
assertEq(b32sPointer, usPointer);
}

function testAsUint256ArrayFromBytes32RetypesInPlace(bytes32[] memory b32s) public pure {
uint256 b32sPointer;
assembly ("memory-safe") {
b32sPointer := b32s
}

uint256[] memory us = LibCast.asUint256Array(b32s);

uint256 usPointer;
assembly ("memory-safe") {
usPointer := us
}
assertEq(usPointer, b32sPointer);
}

/// Writing through either name is visible through the other, because they
/// are one buffer.
function testAsAddressesArrayWritesAreShared(uint256[] memory us, address written) public pure {
vm.assume(us.length > 0);

address[] memory addresses = LibCast.asAddressesArray(us);
addresses[0] = written;

assertEq(us[0], uint256(uint160(written)));
}
}
Loading