Splitting bytes9 into 3 parts

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

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.)

• 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`. 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