-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from iFrostizz/feat/Bytes
feat: Bytes
- Loading branch information
Showing
4 changed files
with
447 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/// @title Bytes | ||
/// @notice SPDX-License-Identifier: MIT | ||
/// @author Franfran <https://github.com/iFrostizz> | ||
/// @notice Low-level operations on bytes | ||
/// @notice Adapted from BytesLib (https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol) | ||
|
||
/// @notice Concatenate two bytes arrays | ||
/// @notice Takes in two pointers of the bytes to concatenate that must be sorted | ||
/// @return Pointer of the new appended concatenated bytes array in the memory | ||
/// @dev Warning! This assumes that the pointer in the memory of the second bytes chunk is after mem_ptr1 + 0x20 | ||
#define macro CONCAT_MEMORY() = takes(2) returns(1) { | ||
// input stack // [mem_ptr1, mem_ptr2] | ||
|
||
// setup stack and memory for the next iterations | ||
dup2 mload swap1 // [mem_ptr1, len2, mem_ptr2] | ||
msize swap1 // [mem_ptr1, free_loc_pos, len2, mem_ptr2] | ||
dup1 mload dup4 // [len2, len1, mem_ptr1, free_loc_pos, len2, mem_ptr2] | ||
dup2 add msize mstore // [len1, mem_ptr1, free_loc_pos, len2, mem_ptr2] | ||
|
||
swap1 0x20 add // [index(i), len1, free_loc_pos, len2, mem_ptr2] | ||
msize // [index(j), index(i), len1, free_loc_pos, len2, mem_ptr2] | ||
swap2 0x00 // [is_sec_loop, len1, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
|
||
// i is the index where we get (mload) the array element and j is the index where we store (mstore) the array at j | ||
loop: // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
dup2 iszero empty_slot jumpi // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
|
||
dup3 mload // [word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
dup3 0x20 gt iszero // [is_full_slot, word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
full_slot jumpi // [word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
|
||
// else it's not a full slot, we're hitting an end. Then clean memory slot and update j with a partial length | ||
dup3 0x20 sub // [pad_len, word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
0x08 mul swap1 dup2 // [shift, word, shift, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
shr // [left_padded_word, shift, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
swap1 shl // [clean_word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
dup5 mstore // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
swap3 add swap2 // [is_sec_loop, index(i), index(j + 1), free_loc_pos, len2, mem_ptr2] | ||
|
||
// here we check if current loop is for the 2nd array | ||
swap1 pop // [is_sec_loop, index(j + 1), free_loc_pos, len2, mem_ptr2] | ||
iszero bridge jumpi // [index(j + 1), free_loc_pos, len2, mem_ptr2] | ||
pop break jump // [free_loc_pos, len2, mem_ptr2] | ||
|
||
empty_slot: // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
swap2 pop pop // [is_sec_loop, index(j), free_loc_pos, len2, mem_ptr2] | ||
iszero bridge jumpi // [index(j), free_loc_pos, len2, mem_ptr2] | ||
pop break jump // [free_loc_pos, len2, mem_ptr2] | ||
|
||
bridge: // [index(j), free_loc_pos, len2, mem_ptr2] | ||
dup4 0x20 add // [index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
dup5 // [len2, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
0x01 // [is_sec_loop, len2, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
loop jump | ||
|
||
full_slot: // [word, is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
dup5 mstore // [is_sec_loop, len_left, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
swap1 0x20 swap1 sub // [len_left - 0x20, is_sec_loop, index(i), index(j), free_loc_pos, len2, mem_ptr2] | ||
swap2 0x20 add // [index(i + 1), is_sec_loop, len_left - 0x20, index(j), free_loc_pos, len2, mem_ptr2] | ||
swap3 0x20 add // [index(j + 1), is_sec_loop, len_left - 0x20, index(i + 1), free_loc_pos, len2, mem_ptr2] | ||
swap3 swap2 swap1 // [is_sec_loop, len_left - 0x20, index(i + 1), index(j + 1), free_loc_pos, len2, mem_ptr2] | ||
loop jump | ||
|
||
break: // [free_loc_pos, len2, mem_ptr2] | ||
swap2 pop pop // [free_loc_pos] | ||
} | ||
|
||
/// @param Pointer in memory of the start of the bytes array | ||
/// @param Start position of the slice relative to the array | ||
/// @param Length of the output slice | ||
/// @return Pointer of the new appended concatenated bytes array in the memory | ||
/// @dev Warning! This assumes that the length of the output slice is less or equal the length of the bytes array (bytes.len < slice.len) | ||
/// @dev Warning! This assumes that the start of the bytes array is not out of bounds (start < len + mem_ptr) | ||
#define macro SLICE_MEMORY() = takes(3) returns(1) { | ||
// input stack // [mem_ptr, start, length] | ||
|
||
msize dup4 msize mstore // [free_loc_pos, mem_ptr, start, length] | ||
msize swap4 // [length, free_loc_pos, mem_ptr, start, index(j)] | ||
// index(i) = mem_ptr + start + 0x20 | ||
swap1 swap3 // [start, length, mem_ptr, free_loc_pos, index(j)] | ||
swap1 swap2 // [mem_ptr, start, length, free_loc_pos, index(j)] | ||
0x20 add add // [index(i), length, free_loc_pos, index(j)] | ||
|
||
// we load our slice chunk at i and store it in a free memory location at j | ||
loop: // [index(i), length_left, free_loc_pos, index(j)] | ||
dup1 mload // [slice_chunk, index(i), length_left, free_loc_pos, index(j)] | ||
|
||
// if current is not full slot, then load the last bytes and break | ||
0x20 dup4 lt // [is_not_full_slot, slice_chunk, index(i), length_left, free_loc_pos, index(j)] | ||
break jumpi // [slice_chunk, index(i), length_left, free_loc_pos, index(j)] | ||
|
||
dup5 mstore // [index(i), length_left, free_loc_pos, index(j)] | ||
|
||
0x20 add swap3 // [free_loc, length_left, free_loc_pos, index(i+1)] | ||
0x20 add swap3 // [index(i+1), length_left, free_loc_pos, free_loc + 1] | ||
swap1 0x20 // [0x20, length_left, index(i+1), free_loc_pos, free_loc + 1] | ||
swap1 sub // [length_left - 1, index(i+1), free_loc_pos, free_loc + 1] | ||
swap1 // [index(i+1), length_left - 1, free_loc_pos, free_loc + 1] | ||
|
||
loop jump | ||
|
||
break: // [slice_chunk, index(i), length, free_loc_pos, index(j)] | ||
// store the remaining length | ||
dup3 0x20 sub // [zero_length, slice_chunk, index(i), length, free_loc_pos, index(j)] | ||
0x08 mul swap1 dup2 // [shift, slice_chunk, shift, index(i), length, free_loc_pos, index(j)] | ||
shr // [left_pad_slice, shift, index(i), length, free_loc_pos, index(j)] | ||
swap1 shl // [slice_chunk, index(i), length, free_loc_pos, index(j)] | ||
dup5 mstore // [index(i), length, free_loc_pos, index(j)] | ||
|
||
pop pop swap1 pop // [free_loc_pos] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.15; | ||
|
||
import "foundry-huff/HuffDeployer.sol"; | ||
import "forge-std/Test.sol"; | ||
import "forge-std/console2.sol"; | ||
|
||
interface IBytes { | ||
function concatMemoryAndSet1() external; | ||
function concatMemoryAndSet2() external; | ||
function concatMemoryAndSet3() external; | ||
function concatMemoryAndSet4() external; | ||
function concatMemoryAndSet5() external; | ||
function concatMemoryAndSet6() external; | ||
function sliceMemoryAndSet1() external; | ||
function sliceMemoryAndSet2() external; | ||
function sliceMemoryAndSet3() external; | ||
} | ||
|
||
contract BytesTest is Test { | ||
IBytes b; | ||
|
||
function setUp() public { | ||
string memory instantiable_code = vm.readFile( | ||
"test/data-structures/mocks/BytesWrappers.huff" | ||
); | ||
|
||
// Create an Instantiable Arrays | ||
HuffConfig config = HuffDeployer.config().with_code(instantiable_code); | ||
b = IBytes(config.deploy("data-structures/Bytes")); | ||
} | ||
|
||
function testConcat1() public { | ||
b.concatMemoryAndSet1(); | ||
|
||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(64))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(0xbabe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe)); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(0xbabe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe)); | ||
} | ||
|
||
function testConcat2() public { | ||
b.concatMemoryAndSet2(); | ||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(96))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(0xbabe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe)); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(0xbabe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe)); | ||
assertEq(vm.load(address(b), bytes32(uint256(96))), bytes32(0xbabe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe2babe)); | ||
} | ||
|
||
function testConcat3() public { | ||
b.concatMemoryAndSet3(); | ||
|
||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(32))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(0xbabe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe)); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(0)); | ||
} | ||
|
||
function testConcat4() public { | ||
b.concatMemoryAndSet4(); | ||
|
||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(37))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(0xbabe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe)); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(bytes5(0xbabe2babe2))); | ||
} | ||
|
||
function testConcat5() public { | ||
b.concatMemoryAndSet5(); | ||
|
||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(15))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(bytes15(0xbabe1babe1babe1babe1babe2babe2))); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(0)); | ||
} | ||
|
||
function testConcat6() public { | ||
b.concatMemoryAndSet6(); | ||
|
||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(15))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(bytes15(0xbabe1babe1babe1babe1babe2babe2))); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(0)); | ||
} | ||
|
||
function testSlice1() public { | ||
b.sliceMemoryAndSet1(); | ||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(16))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(bytes16(0xbabe1babe1babe1babe1babe1babe1ba))); | ||
} | ||
|
||
function testSlice2() public { | ||
b.sliceMemoryAndSet2(); | ||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(36))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(0x1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babe1babebabe)); | ||
assertEq(vm.load(address(b), bytes32(uint256(64))), bytes32(bytes4(0x2babe2ba))); | ||
} | ||
|
||
function testSlice3() public { | ||
b.sliceMemoryAndSet3(); | ||
assertEq(vm.load(address(b), bytes32(0)), bytes32(uint256(4))); | ||
assertEq(vm.load(address(b), bytes32(uint256(32))), bytes32(bytes4(0xbabe2bab))); | ||
} | ||
} |
Oops, something went wrong.