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);
    }
}
1
  • Since the halfs are just the first / last 16 bytes there should be an efficient way, like using inline assembly and mload?
    – floyd
    Commented Mar 16, 2017 at 15:16

4 Answers 4

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.

4
  • Just saw this now. Thank you very much, will compare now to my own solution posted below.
    – floyd
    Commented Mar 16, 2017 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
    Commented Aug 20, 2017 at 23:00
  • I don't understand why the second mstore doesn't need to also increment the source address.
    – jdbertron
    Commented Dec 19, 2022 at 21:46
  • @jdbertron, this code worked on ancient version of Solidity. See another answer (with type conversions) for modern solution. Commented Dec 21, 2022 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))
    }
  }
}
3
  • +1, that is nice. but can you explain a bit what mload(0x40) means? Commented Mar 16, 2017 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
    Commented Mar 18, 2017 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
    Commented Aug 20, 2017 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)

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.