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.

silkAdmin
  • 103
  • 2
  • 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 Feb 03 '22 at 19:37
  • @Jesbus I see, so knowing that, how would one go about doing this bytes wise operations – silkAdmin Feb 03 '22 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 Feb 04 '22 at 10:02
  • 1
    thanks for mentioning this @Jesbus, I might actually skip assembly for now. – silkAdmin Feb 04 '22 at 23:21

1 Answers1

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

}

hroussille
  • 7,661
  • 2
  • 6
  • 29
  • 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 Feb 03 '22 at 20:16
  • Also why bytes(10), couldn't it be 32? – silkAdmin Feb 03 '22 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 Feb 03 '22 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 Feb 04 '22 at 23:24