1

From the example here, I could split bytes32 to bytes16. But I am unable to use a similar approach to split bytes9 into three parts. Can someone help me understand what am I doing wrong?

//working
    function split2(bytes32 source) constant returns (bytes16, bytes16){
        bytes16[2] memory y = [bytes16(0), 0];
        assembly {
            mstore(y, source)
            mstore(add(y, 16), source)
        }
        return (y[0], y[1]);
    }

//not working
    function split3(bytes9 source) constant returns (bytes3, bytes3, bytes3){
        bytes3[3] memory y = [bytes3(0), 0, 0];
        assembly {
            mstore(y, source)
            mstore(add(y, 3), source)
            mstore(add(y, 6), source)
        }
        return (y[0], y[1], y[2]);
    }

1 Answer 1

3

Give this a whirl:

function split3(bytes9 source) public pure returns(bytes3, bytes3, bytes3) {
    bytes3[3] memory x = [bytes3(0), 0, 0];

    assembly {
        mstore(x, source)
        mstore(add(x, 29), source)
        mstore(add(x, 58), source)
    }
    return (x[0], x[1], x[2]);
}

The problem is that your address offsets are going the wrong way.

This is what you are aiming for in the x array (one column per byte):

0         1         2         3         4         5         6         7         8         9
012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
<--- x[0] ---------------------><--- x[1] ---------------------><--- x[2] --------------------->
SSSSSSSSS                    SSSSSSSSS                    SSSSSSSSS
111                             222                             333

Each element of x is actually a 32-byte word, despite appearances. SSSSSSSSS is your bytes9 source. 111 is your first bytes3 returned, 222 is your second bytes3 returned, and 333 is your third. Solidity takes care of masking off the redundant extra bytes that are set outside the bytes3 quantities.

(This technique is a bit of a nasty hack, IMHO, and confusing to boot.)

3
  • 1
    Great answer. It took me a while to understand the offsets, though the diagram does help. For others struggling like I did: the start of the first bytes3 in the array is at x+0. The second is at x+32, and the third is at x+64. In the source, the offset of the first three bytes is 0, the second three bytes is 3, and the third three bytes is 6. So to copy everything to the right place, we take x and add the offset of the bytes3 we care about and then subtract the offset of the bytes we care about in the source. The offsets are then x+0-0, x+32-3, and x+64-6.
    – user19510
    Commented Jan 5, 2018 at 19:32
  • Thank you @benjaminion and smarx. Both answers helped me understand. benjaminion, you mentioned, this to be a bad hack. What would be the right way? I just discovered passing strings as function argument is a bad idea becasue of gas issues, so I am replacing string with bytesn. Do you suggest, I just stick to bytes32?
    – Titu
    Commented Jan 5, 2018 at 20:29
  • At the moment there is no really efficient work around for all cases. At some point the EVM will have a SHR instruction added, so it will be really cheap to do, say, x = (source >> 24) & 0xffffff to extract bytes 3-5 (you can do this today, but the cost may be high if the compiler decides to do >> with an EXP opcode, which it sometimes does. For certain cases, something like x = (source / 0x1000000)&0xffffff could be efficient. Commented Jan 5, 2018 at 21:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.