0

I want to split bytes32 into multiples of 8 (in assembly). I have the following code(After referring here):

function testAssembly(bytes32 _msg) public pure returns (bytes8 i, bytes8 j, bytes8 k, uint256 m, bytes memory size){
      assembly {
        let i_ptr := mload(0x40)
        let j_ptr := mload(0x62)
        let k_ptr := mload(0x92)

        m := calldataload(4)
        //size := msize
        calldatacopy(i_ptr,0x4,8)
        calldatacopy(j_ptr,0x9,8)
        calldatacopy(k_ptr,0x11,8)

        i := mload(i_ptr)
        j := mload(j_ptr)
        k := mload(k_ptr)
      }
      return (i,j,k,m,size);
    }

 function testAssemblyArr(bytes32 _msg) public pure returns (bytes8 i, bytes8 j, bytes8 k, uint256 m, bytes memory size){

      bytes8[3] memory y = [bytes8(0), 0,0];

      assembly {

        m := calldataload(4)
        //size := msize         //(*)
        mstore(y,_msg)
        mstore(add(y,0x8),_msg)
        mstore(add(y,0x10),_msg)
      }
      return (y[0],y[1],y[2],m,size);
    }  

I have following input (Remix): "0x1212121212121212343434343434343456565656565656567878787878787878"

Expected output(for both):

0: bytes8: i 0x1212121212121212
1: bytes8: j 0x3434343434343434
2: bytes8: k 0x5656565656565656

Observed output:

testAssembly:
0: bytes8: i 0x1212121212121212
1: bytes8: j 0x3434345656565656
2: bytes8: k 0x3434345656565656
3: uint256: m 8173559240281143207206663486900115636630769273905500857318033424334900983928
4: bytes: size 0x

testAssemblyArr:
0: bytes8: i 0x1212121212121212
1: bytes8: j 0x5656565656565656
2: bytes8: k 0x0000000000000000
3: uint256: m 8173559240281143207206663486900115636630769273905500857318033424334900983928
4: bytes: size 0x

If i change function parameter of testAssemblyArr to bytes then it gives the following output:

0: bytes8: i 0x0000000000000000
1: bytes8: j 0x0000000000000000
2: bytes8: k 0x0000000000000000
3: uint256: m 32
4: bytes: size 0x

In the above output, (Ignore m, it gives correct output if its return type is changed to bytes32). For calldatacopy() function, I used both integer and hexadecimal values but it gives incorrect values. How should i go about this to get expected results?

Also when you uncomment line *(msize), it gives "out of gas" error in ganache and crashes the browser(tab) in Remix.

pebble
  • 153
  • 1
  • 10

1 Answers1

2

You do not have to complicate it using assembly, using shift and type cast should work.

pragma solidity >=0.4.22 <0.6.0;

contract A {

    function test(bytes32 r) public pure returns (bytes8 i, bytes8 j, bytes8 k, bytes8 m) {
        uint256 rr = uint256(r);
        i = bytes8(uint64(rr >> 64*3));
        j = bytes8(uint64(rr >> 64*2));
        k = bytes8(uint64(rr >> 64*1));
        m = bytes8(uint64(rr >> 64*0));
    }

    function bar(bytes32 r) public pure returns (bytes8 i, bytes8 j, bytes8 k, bytes8 m) {
        i = bytes8(r);
        j = bytes8(r << 64*1);
        k = bytes8(r << 64*2);
        m = bytes8(r << 64*3);
    }

    function foo(bytes32 r) public pure returns (bytes8 i, bytes8 j, bytes8 k, bytes8 m) {
        assembly {
            i := r
            j := shl(64, r)
            k := shl(128, r)
            m := shl(192, r)
        }
    }
}
Ismael
  • 30,570
  • 21
  • 53
  • 96
  • Your solution works, but i need an assembly solution since I am learning the language. I saw a code here, which uses assembly to get r,s,v values. – pebble Feb 27 '19 at 09:30
  • It should be straightforward to port the code to assembly, you do not need cast types because everything is 32 bytes. – Ismael Feb 27 '19 at 16:00
  • I tried this: assembly{ i := shr(192,r) j := shr(128,r) k := shr(64,r) m := shr(0,r) } But it gives zero value for i,j,k – pebble Mar 07 '19 at 05:22
  • @pebble I've included two new implementations. The problem with the previous implementation is how assignment works: uintXXX uses the rightmost bytes, and bytesXXX uses the leftmost bytes. Assembly treat all data as bytes32/uint256, so to cast to bytes8/uint64 you need to do extra operations. – Ismael Mar 08 '19 at 02:55