Skip to content

Commit

Permalink
✨ LibBytes sliceCalldata and emptyCalldata (#1186)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized authored Nov 27, 2024
1 parent 42c4786 commit 3d0f41a
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/utils/LibBytes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,37 @@ library LibBytes {
result = slice(subject, start, type(uint256).max);
}

/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, end), sub(end, start))
}
}

/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, subject.length), sub(subject.length, start))
}
}

/// @dev Reduces the size of `subject` to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncate(bytes memory subject, uint256 n)
Expand Down Expand Up @@ -617,4 +648,12 @@ library LibBytes {
result := calldataload(add(a.offset, offset))
}
}

/// @dev Returns empty calldata bytes. For silencing the compiler.
function emptyCalldata() internal pure returns (bytes calldata result) {
/// @solidity memory-safe-assembly
assembly {
result.length := 0
}
}
}
39 changes: 39 additions & 0 deletions src/utils/g/LibBytes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,37 @@ library LibBytes {
result = slice(subject, start, type(uint256).max);
}

/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start, uint256 end)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
end := xor(end, mul(xor(end, subject.length), lt(subject.length, end)))
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, end), sub(end, start))
}
}

/// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes.
/// `start` is a byte offset. Faster than Solidity's native slicing.
function sliceCalldata(bytes calldata subject, uint256 start)
internal
pure
returns (bytes calldata result)
{
/// @solidity memory-safe-assembly
assembly {
start := xor(start, mul(xor(start, subject.length), lt(subject.length, start)))
result.offset := add(subject.offset, start)
result.length := mul(lt(start, subject.length), sub(subject.length, start))
}
}

/// @dev Reduces the size of `subject` to `n`.
/// If `n` is greater than the size of `subject`, this will be a no-op.
function truncate(bytes memory subject, uint256 n)
Expand Down Expand Up @@ -621,4 +652,12 @@ library LibBytes {
result := calldataload(add(a.offset, offset))
}
}

/// @dev Returns empty calldata bytes. For silencing the compiler.
function emptyCalldata() internal pure returns (bytes calldata result) {
/// @solidity memory-safe-assembly
assembly {
result.length := 0
}
}
}
15 changes: 15 additions & 0 deletions test/LibBytes.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,19 @@ contract LibBytesTest is SoladyTest {
bytes memory truncated = LibBytes.truncatedCalldata(a, n);
assertEq(truncated, sliced);
}

function testSliceCalldata(bytes calldata a, uint256 start, uint256 end) public {
bytes memory aCopy = a;
assertEq(LibBytes.sliceCalldata(a, start, end), LibBytes.slice(aCopy, start, end));
assertEq(LibBytes.sliceCalldata(a, start), LibBytes.slice(aCopy, start));
}

function testSliceCalldata() public {
bytes memory data = hex"12f712c77281c66267d947165237893ba5eca3e5481727fe76d4511ce1b564f5";
this.testSliceCalldata(data, 1, 11);
}

function testEmptyCalldata() public {
assertEq(LibBytes.emptyCalldata(), "");
}
}

0 comments on commit 3d0f41a

Please sign in to comment.