0

I am learning assembly and I am trying to write to individual bytes in a bytes32 array. I have the following code:

contract TestAssembly {}
    function test(
        uint8 _count
    ) public {
        bytes32 bytesTest;
        assembly { 
            for { let i := 0} lt(i, _count) { i := add(i, 1) } { 
                mstore(add(bytesTest, i), 0x33)
            }
        }
    }
}

I was hoping that this code would iterate through each bytes in bytesTest, and replace each by 0x33, however bytesTest doesn't seem be modified.

4
  • 2
    bytes1 through bytes32 local variables are stored on the stack, not in memory, so they cannot be manipulated with mstore. bytes memory bytesTest; is stored in memory.
    – Jesbus
    Commented Feb 3, 2022 at 19:37
  • @Jesbus I see, so knowing that, how would one go about doing this bytes wise operations
    – silkAdmin
    Commented Feb 3, 2022 at 19:54
  • 1
    You can use bitwise ops like SHL, SHR, OR, AND, BYTE to manipulate a value on the stack byte by byte. You don't need assembly to do that. Nota bene: mstore writes 32 bytes of data to memory. mstore8 can be used to only write a single byte.
    – Jesbus
    Commented Feb 4, 2022 at 10:02
  • 1
    thanks for mentioning this @Jesbus, I might actually skip assembly for now.
    – silkAdmin
    Commented Feb 4, 2022 at 23:21

1 Answer 1

2

Building up on @Jesbus comment, here is a commented example achieving what you want :

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract TestAssembly {
    function test(uint8 _count) public pure returns (bytes memory) {

        // Declares the array in memory
        bytes memory bytesTest = new bytes(10);

        // Protect against buffer overflow
        require(_count <= 10, "not enough bytes in the array");

        assembly {

            // Skip the first 32 bytes reserved for the length of the memory array
            let offset := add(bytesTest, 0x20)

            // Loop _count times over the array
            for { let i := 0} lt(i, _count) { i := add(i, 1) } { 
                // Make use of mstore8 (1 byte) instead of mstore (32 bytes)
                mstore8(add(offset, i), 0x33)
            }
        }

        return bytesTest;
    }
}

EDIT :

I'm adding an example relying on array literals where there is no length prefix and using bytes32.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract TestAssembly {
    function test(uint8 _count) public pure returns (bytes32) {

        // Declares the byte32 array in memory : this is an array literal -> there is no length prefix
        bytes32[1] memory bytesTest = [bytes32(0)];

        // Protect against buffer overflow
        require(_count <= 32, "not enough bytes in the array");

        assembly {
            // Loop _count times over the array
            for { let i := 0} lt(i, _count) { i := add(i, 1) } { 
                // Make use of mstore8 (1 byte) instead of mstore (32 bytes)
                mstore8(add(bytesTest, i), 0x33)
            }
        }

        return bytesTest[0];
    }
}

Plus a third example not relying on solidity arrays but directly allocating / computing in memory through assembly.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract TestAssembly {
    function test(uint8 _count) public pure returns (bytes32) {

        bytes32 result;

        // Protect against buffer overflow
        require(_count <= 32, "not enough bytes in the array");

        assembly {
            
            // Allocate 32 bytes from the free memory pointer
            let buffer := mload(0x40)

            // Increase the free memory pointer to account for our buffer
            mstore(0x40, add(buffer, 0x20))

            // Loop _count times over the array
            for { let i := 0} lt(i, _count) { i := add(i, 1) } { 
                // Make use of mstore8 (1 byte) instead of mstore (32 bytes)
                mstore8(add(buffer, i), 0x33)
            }

            result := mload(buffer)

            // Decrease the free memory pointer to account for our buffer
            mstore(0x40, sub(buffer, 0x20))
        }

        return result;
    }
}
4
  • Thanks. So is it not possible to manipulate a bytes32, byte by byte ? I would rather store bytes32 as then I dont have the overhead of storing the array size
    – silkAdmin
    Commented Feb 3, 2022 at 20:16
  • Also why bytes(10), couldn't it be 32?
    – silkAdmin
    Commented Feb 3, 2022 at 21:05
  • 1
    10 was completely arbitrary. I'll add an example with a bytes32 array literal (that doesn't require any length prefix).
    – hroussille
    Commented Feb 3, 2022 at 21:24
  • Thanks for the detailed answer. It's starting to make sense now. I am leaving the question open until I am done with the code in case I find anything useful to add.
    – silkAdmin
    Commented Feb 4, 2022 at 23:24

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.