3

I'm learning Solidity Assembly and I'm confused about something. I'm looking at this library called Seriality. Specifically, this function: https://github.com/pouladzade/Seriality/blob/master/src/TypesToBytes.sol#L21

function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure {
    assembly {
        mstore(add(_output, _offst), _input)
        mstore(add(add(_output, _offst),32), add(_input,32))
    }
}

That function bytes32ToBytes takes a bytes32 variable and stores it in a dynamically sized bytes array, starting at the offset passed in.

The thing that confuses me is that it uses the mstore function twice. But the mstore function stores a word, which is 32 bytes, right? So why is it called twice, given that the input is 32 bytes? Wouldn't calling it twice store 2 words, which is 64 bytes?

Thanks!

user1558646
  • 145
  • 4
  • 9

2 Answers2

5

Solidity arrays are stored by writing out the size of the array to the first storage slot then writing out the data to the subsequent slots.

Knowing that mstore has the following parameters: mstore(START_LOCATION, ITEM_TO_STORE), the first mstore statement is written as follows:

mstore(add(_output, _offst), _input)

Since the first slot of the array points to the size of the array, this statement is setting the size of _output. You should be able to get the same result by replacing it with mstore(add(_output, _offst), 32) (since the size is of _input is static).

The second statement (mstore(add(add(_output, _offst),32), add(_input,32))) is the one that writes the data itself. Here, we are shifting the position of both pointers by 32 bytes (as the first 32 bytes for both arrays are pointing to the size) and storing the value of _input to where the data is stored for _output.

Chances are, _output will already be initialized before calling this method (so the length will already be set), so it will usually be unnecessary. But, it doesn't hurt. Note that a similar implementation making this assumption would look like this:

function test() public pure returns (bytes) {
    bytes32 i = "some message";
    bytes memory o = new bytes(32); // Initializing this way sets the length to the location "o" points to. This replaces mstore(add(_output, _offst), _input).
    bytes32ToBytes(0, i, o);
    
    return o;
}

function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure {
    assembly {
        mstore(add(add(_output, _offst),32), add(_input,32))
    }
}
Pottedtree
  • 7
  • 2
  • 2
Adam Kipnis
  • 10,175
  • 10
  • 35
  • 48
0

Not sure about the intention of the function bytes32ToBytes

If it is turning a bytes32 into a bytes, I think the right implementation should be

pragma solidity ^0.7.0;

contract DecodeEncode {
    
    function test() public pure returns (bytes memory) {
        bytes32 i = "some message";
        bytes memory o = new bytes(32); // Initializing this way sets the length to the location "o" points to. This replaces mstore(add(_output, _offst), _input).
        bytes32ToBytes(0, i, o);
        
        return o;
    }

    function bytes32ToBytes(uint _offst, bytes32 _input, bytes memory _output) internal pure {
        assembly {
            mstore(add(_output, _offst), 32) //lineA
            mstore(add(add(_output, _offst), 32), _input) //lineB
        }
    }

}

lineA sets the length of the bytes as 32 bytes
lineB sets the content of the first slot of the bytes as _input

DiveInto
  • 2,158
  • 3
  • 27
  • 50