Skip to content

Commit

Permalink
feat: add getAfterStrict, more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dk1a committed Dec 7, 2022
1 parent b397f9e commit 2e5d62b
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 42 deletions.
21 changes: 18 additions & 3 deletions src/Slice.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ using {
cmp, eq, ne, lt, lte, gt, gte,
// index
get, first, last,
splitAt, getSubslice, getBefore, getAfter,
splitAt, getSubslice, getBefore, getAfter, getAfterStrict,
// search
find, rfind, contains,
startsWith, endsWith,
Expand Down Expand Up @@ -144,6 +144,8 @@ function toBytes32(Slice self) pure returns (bytes32 b) {

/**
* @dev Returns keccak256 of all the bytes of `Slice`.
* Note that for any `bytes memory b`, keccak256(b) == b.toSlice().keccak()
* (keccak256 does not include the length byte)
*/
function keccak(Slice self) pure returns (bytes32 result) {
uint256 selfPtr = self.ptr();
Expand Down Expand Up @@ -349,17 +351,30 @@ function getBefore(Slice self, uint256 index) pure returns (Slice) {

/**
* @dev Returns a subslice [index:] of `self`.
* Reverts if `index` >= length.
* Reverts if `index` > length.
*/
function getAfter(Slice self, uint256 index) pure returns (Slice) {
uint256 selfLen = self.len();
if (index >= selfLen) revert Slice__OutOfBounds();
if (index > selfLen) revert Slice__OutOfBounds();
// safe because index <= selfLen (ptr+len is implicitly safe)
unchecked {
return Slice__.fromRawParts(self.ptr() + index, selfLen - index);
}
}

/**
* @dev Returns a non-zero subslice [index:] of `self`.
* Reverts if `index` >= length.
*/
function getAfterStrict(Slice self, uint256 index) pure returns (Slice) {
uint256 selfLen = self.len();
if (index >= selfLen) revert Slice__OutOfBounds();
// safe because index < selfLen (ptr+len is implicitly safe)
unchecked {
return Slice__.fromRawParts(self.ptr() + index, selfLen - index);
}
}

/**
* @dev Returns the byte index of the first slice of `self` that matches `pattern`.
* Returns type(uint256).max if the `pattern` does not match.
Expand Down
2 changes: 1 addition & 1 deletion src/StrSlice.sol
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ function isCharBoundary(StrSlice self, uint256 index) pure returns (bool) {
* Reverts if index is out of bounds.
*/
function get(StrSlice self, uint256 index) pure returns (StrChar char) {
bytes32 b = self.asSlice().getAfter(index).toBytes32();
bytes32 b = self.asSlice().getAfterStrict(index).toBytes32();
if (!isValidUtf8(b)) revert StrSlice__InvalidCharBoundary();
return StrChar__.fromValidUtf8(b);
}
Expand Down
125 changes: 87 additions & 38 deletions test/Slice.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,52 @@ contract SliceTest is PRBTest, Assertions {
}

// skipping cmp tests, that's covered by SliceAssertionsTest
// TODO explicitly test stuff like copyFromSlice,startsWith etc
// (tho it's likely fine due to other tests)

function testLen(bytes calldata _b) public {
assertEq(_b.toSlice().len(), _b.length);
}

function testIsEmpty() public {
assertTrue(bytes("").toSlice().isEmpty());
assertFalse(new bytes(1).toSlice().isEmpty());
}

function testToBytes(bytes calldata _b) public {
assertEq(_b, _b.toSlice().toBytes());
}

function testToBytes32(bytes memory _b) public {
bytes32 b32;
if (_b.length > 0) {
/// @solidity memory-safe-assembly
assembly {
b32 := mload(add(_b, 0x20))
}
}
assertEq(b32, _b.toSlice().toBytes32());
}

function testKeccak__Eq(bytes calldata _b) public {
bytes memory b1 = _b;
bytes memory b2 = _b;

assertEq(b1.toSlice().keccak(), b2.toSlice().keccak());
assertEq(keccak256(b1), keccak256(b2));
assertEq(b1.toSlice().keccak(), keccak256(b1));
}

function testKeccak__NotEq(bytes calldata _b) public {
vm.assume(_b.length > 0);
bytes memory b1 = _b;
bytes memory b2 = _b;

uint256 i = uint256(keccak256(abi.encode(_b, "i"))) % _b.length;
b1[i] ^= 0x01;
assertEq(b1.toSlice().keccak(), keccak256(b1));
assertNotEq(b1.toSlice().keccak(), b2.toSlice().keccak());
assertNotEq(keccak256(b1), keccak256(b2));
}

/*//////////////////////////////////////////////////////////////////////////
CONCATENATION
//////////////////////////////////////////////////////////////////////////*/
Expand All @@ -34,10 +73,7 @@ contract SliceTest is PRBTest, Assertions {
bytes memory b1 = _b[:_b.length / 2];
bytes memory b2 = _b[_b.length / 2:];

assertEq(
keccak256(b1.toSlice().add(b2.toSlice())),
keccak256(_b)
);
assertEq(b1.toSlice().add(b2.toSlice()), _b);
}

function testJoin__EmptySeparator(bytes calldata _b) public {
Expand All @@ -49,10 +85,7 @@ contract SliceTest is PRBTest, Assertions {
slices[0] = b1.toSlice();
slices[1] = b2.toSlice();

assertEq(
keccak256(sep.toSlice().join(slices)),
keccak256(_b)
);
assertEq(sep.toSlice().join(slices), _b);
}

function testJoin__RandomSeparator(bytes calldata _b) public {
Expand All @@ -66,10 +99,7 @@ contract SliceTest is PRBTest, Assertions {
slices[1] = b2.toSlice();
slices[2] = b3.toSlice();

assertEq(
keccak256(sep.toSlice().join(slices)),
keccak256(abi.encodePacked(b1, sep, b2, sep, b3))
);
assertEq(sep.toSlice().join(slices), abi.encodePacked(b1, sep, b2, sep, b3));
}

function testJoin__ArrayLen1(bytes calldata _b) public {
Expand All @@ -79,21 +109,15 @@ contract SliceTest is PRBTest, Assertions {
Slice[] memory slices = new Slice[](1);
slices[0] = b1.toSlice();

assertEq(
keccak256(sep.toSlice().join(slices)),
keccak256(abi.encodePacked(b1))
);
assertEq(sep.toSlice().join(slices), abi.encodePacked(b1));
}

function testJoin__ArrayLen0() public {
bytes memory sep = hex'ABCD';

Slice[] memory slices;

assertEq(
keccak256(sep.toSlice().join(slices)),
keccak256('')
);
assertEq(sep.toSlice().join(slices), '');
}

/*//////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -124,30 +148,24 @@ contract SliceTest is PRBTest, Assertions {
Slice slice = _b.toSlice();
(Slice s1, Slice s2) = slice.splitAt(_b.length / 2);
assertEq(
keccak256(abi.encodePacked(
s1.copyToBytes(), s2.copyToBytes()
)),
keccak256(_b)
abi.encodePacked(
s1.toBytes(), s2.toBytes()
),
_b
);
}

function testSplitAt__0(bytes calldata _b) public {
Slice slice = _b.toSlice();
(Slice s1, Slice s2) = slice.splitAt(0);
assertEq(
keccak256(s2.copyToBytes()),
keccak256(_b)
);
assertEq(s2.toBytes(), _b);
assertEq(s1.len(), 0);
}

function testSplitAt__Length(bytes calldata _b) public {
Slice slice = _b.toSlice();
(Slice s1, Slice s2) = slice.splitAt(_b.length);
assertEq(
keccak256(s1.copyToBytes()),
keccak256(_b)
);
assertEq(s1.toBytes(), _b);
assertEq(s2.len(), 0);
}

Expand All @@ -157,10 +175,7 @@ contract SliceTest is PRBTest, Assertions {
uint256 end = _b.length == 0 ? 0 : uint256(keccak256(abi.encode(_b, "end"))) % _b.length;
vm.assume(start <= end);
Slice subslice = _b.toSlice().getSubslice(start, end);
assertEq(
keccak256(subslice.copyToBytes()),
keccak256(_b[start:end])
);
assertEq(subslice.toBytes(), _b[start:end]);
}

function testGetSubslice__RevertStartAfterEnd(bytes calldata _b) public {
Expand All @@ -172,6 +187,40 @@ contract SliceTest is PRBTest, Assertions {
_b.toSlice().getSubslice(start, end);
}

function testGetBefore(bytes calldata _b) public {
Slice s1 = _b.toSlice().getBefore(_b.length / 2);
assertEq(s1, _b[:_b.length / 2]);
}

function testGetBefore_RevertOutOfBounds() public {
bytes memory _b;
vm.expectRevert(Slice__OutOfBounds.selector);
_b.toSlice().getBefore(1);
}

function testGetAfter(bytes calldata _b) public {
Slice s1 = _b.toSlice().getAfter(_b.length / 2);
assertEq(s1, _b[_b.length / 2:]);
}

function testGetAfter_RevertOutOfBounds() public {
bytes memory _b;
vm.expectRevert(Slice__OutOfBounds.selector);
_b.toSlice().getAfter(1);
}

function testGetAfterStrict(bytes calldata _b) public {
vm.assume(_b.length > 0);
Slice s1 = _b.toSlice().getAfterStrict(_b.length / 2);
assertEq(s1, _b[_b.length / 2:]);
}

function testGetAfterStrict_RevertOutOfBounds() public {
bytes memory _b;
vm.expectRevert(Slice__OutOfBounds.selector);
_b.toSlice().getAfterStrict(0);
}

/*//////////////////////////////////////////////////////////////////////////
FIND
//////////////////////////////////////////////////////////////////////////*/
Expand Down

0 comments on commit 2e5d62b

Please sign in to comment.