1

I brand new to assembly, and just at the point of writing my first code using it, so please excuse me if I am way off on what I am trying to accomplish.

Here is my code:

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


contract Test {
    function starter() public pure returns(string memory, bytes memory) {
        bytes6 _id = '123456';
        bytes memory b = 'The input _id is ______ and it was replaced using assembly';

        assembly {
            mstore(add(b, 49), _id)
        }

        return (string(b), b);
    }
}

The output looks like this:

0:
string: The input _id is 123456 assembly
1:
bytes: 0x54686520696e707574205f696420697320313233343536000000000000000000000000000000000000000000000000000020617373656d626c79

So I can tell that what it did was store the 6 bytes of _id, plus another 26 null bytes for a total of 32 bytes replaced.

Is it possible to do this without those additional 26 null bytes? Note I am not looking to do this iteratively as the real case will be a much larger string with many more replacements.

3
  • There's mstore8 that stores a single byte.
    – Ismael
    Jul 20, 2022 at 19:28
  • Did you try using abi.encodePacked to concatenate strings?
    – Ismael
    Jul 20, 2022 at 19:37
  • Sure it's possible, are you only targetting bytes6 "slices" / replacement or are you looking for something more generic ?
    – hroussille
    Jul 20, 2022 at 20:23

1 Answer 1

1

Is it possible to do this without those additional 26 null bytes? Note I am not looking to do this iteratively as the real case will be a much larger string with many more replacements.

As I said in the comments, it's totally possible. Here is a simple version working only with bytes6, let me know if you are interested in a more generic "slice assignment" piece of code handling various lengths.

The idea is simply to clear the leading first 6 bytes of the existing data with the and operator and the appropriate mask (0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF in this case, equivalent to not(shl(208, 0xFFFFFFFFFFFF))) and then combining your id (which is actually a left aligned 32 bytes value) with the result of the previous operation with the or operator.

To put it simply, it replace the first 6 bytes at add(b, 49 by the first 6 bytes of _id. All other bytes are left unchanged.

assembly {
    mstore(add(b, 49), or(and(mload(add(b, 49)), not(shl(208, 0xFFFFFFFFFFFF))), _id))
}

The result is 0x54686520696e707574205f69642069732031323334353620616e6420697420776173207265706c61636564207573696e6720617373656d626c79, in ASCII : "The input _id is 123456 and it was replaced using assembly".

I hope this answers your question.

3
  • This is perfect - thank you!!! I would also certainly be interested in a more generic form of this as bytes6 is the most common for my use case though not the only one. Understanding how those inputs (looks like the latter part of the or()) should be modifies for any bytesX would be hugely appreciated! My guess is something like bytes8 would be not(shl(208, 0xFFFFFFFFFFFFFFFF)) but I just want to double check. Jul 20, 2022 at 21:22
  • 1
    Almost, it would be not(shl(192, 0xFFFFFFFFFFFFFFFF)) you have to shift by 16 less bits to the left as you are defining 2 more bytes (so 16 bits). I'll try my to edit my answer with a generic version in the days to come depending on my availability.
    – hroussille
    Jul 20, 2022 at 21:28
  • What would that look like for bytes20?
    – Mila A
    Feb 13 at 13:44

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.