13

I want to split a 32 Byte source: first half in Bytes16 half1; second half in Bytes16 half2. My code works, however only for dynamic array, not with fixed size 16.

pragma solidity ^0.4.8;

contract cut {

    function cutSha(bytes32 source) constant returns (bytes, bytes) {
        bytes memory half1 = new bytes(16);
        bytes memory half2 = new bytes(16);
        for (uint j = 0; j < 16; j++) {
                half1[j] = source[j];
                half2[j] = source[j+16];
        }
        return (half1, half2);
    }
}
floyd
  • 599
  • 1
  • 5
  • 9
  • Since the halfs are just the first / last 16 bytes there should be an efficient way, like using inline assembly and mload? – floyd Mar 16 '17 at 15:16

4 Answers4

10

This code is no longer correct on modern versions of Solidity. Please see Sergey Kaunov's answer.


It is possible to do this with assembly:

pragma solidity ^0.4.8;

contract c { event trace(bytes32 x, bytes16 a, bytes16 b);

function foo(bytes32 source) {
    bytes16[2] memory y = [bytes16(0), 0];
    assembly {
        mstore(y, source)
        mstore(add(y, 16), source)
    }
    trace(source, y[0], y[1]);
}

}

For example, converting bytes from the string "what a wonderful world!", produces this after using 2245 gas:

trace[
  "0x77686174206120776f6e64657266756c20776f726c6421000000000000000000",
  "0x77686174206120776f6e64657266756c",
  "0x20776f726c6421000000000000000000"
]

NB: the code relies on the internal data representation that may be subject to change in later versions of Solidity or interfere with Solidity optimizer in an unpredictable ways.

max taldykin
  • 2,966
  • 19
  • 27
  • Just saw this now. Thank you very much, will compare now to my own solution posted below. – floyd Mar 16 '17 at 20:19
  • How do we change your code so that I can take input of 56 characters and split them into two bytes32? – Curt Aug 20 '17 at 23:00
  • I don't understand why the second mstore doesn't need to also increment the source address. – jdbertron Dec 19 '22 at 21:46
  • @jdbertron, this code worked on ancient version of Solidity. See another answer (with type conversions) for modern solution. – max taldykin Dec 21 '22 at 18:53
4

I found a solution using inline assembly:

contract cutByte32 {

  //"0xa9c40ddcb43ebbc83add97b8f9f361f12b19bceff2f76b68f66b5bb1812365a9"
  //use this as remix command

  function cut(bytes32 sha) constant returns (bytes16 half1, bytes16 half2) {
    assembly {
      let freemem_pointer := mload(0x40)
      mstore(add(freemem_pointer,0x00), sha)
      half1 := mload(add(freemem_pointer,0x00))
      half2 := mload(add(freemem_pointer,0x10))
    }
  }
}
floyd
  • 599
  • 1
  • 5
  • 9
  • +1, that is nice. but can you explain a bit what mload(0x40) means? – max taldykin Mar 16 '17 at 22:56
  • Thank you, at the address 0x40 (solidity definition) the pointer (/address) for the next free slot on the memory is stored. Loading it first into the freemem_pointer variable makes sure I don't overwrite anything in memory. However I assume there are even better solutions. If I would new at what address the parameter value "bytes32 sha" is stored I don't need to store it again and can directly start to read it out 16byte wise. – floyd Mar 18 '17 at 17:45
  • How do we change your code so that I can take input of 56 characters and split them into two bytes32? – Curt Aug 20 '17 at 23:00
2

It's now possible to use type conversions to do it in a couple of lines.

pragma solidity 0.8.16;

contract cut {

function cutSha(bytes32 source) 
    public 
    returns (bytes16 half1, bytes16 half2) 
{
    half1 = bytes16(source);
    half2 = bytes16(uint128(uint256(source)));

}

}

PS Sorry if this code will need any linting -- can't test it right now, this is just illustration for the idea; so any corrections are welcome.

0

Try to do it with uints

   function bytesChunck(bytes32 source, uint start, uint numBytes) constant returns(uint _result){
                uint counter = 0;
                uint result;

                for(uint i = 0; i < numBytes; i++) {
                    result += uint8(source[start + i]);
                }
                return result;
        }

but Reading bytes from bytes32 returns zero value (except first one)

yanik
  • 473
  • 6
  • 12