Assembly – Techniques for Manipulating Individual Bytes

assembly

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.

Best Answer

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;
    }
}
Related Topic