Объединение двухбайтовых массивов с помощью сборки

Кто-нибудь знает, как эффективно объединять двухбайтовые массивы с языком ассемблера, чтобы сэкономить затраты на газ? В настоящее время у меня есть

function mergeBytes(bytes param1, bytes param2) returns (bytes) {

    bytes memory merged = new bytes(param1.length + param2.length);

    uint k = 0;
    for (uint i = 0; i < param1.length; i++) {
        merged[k] = param1[i];
        k++;
    }

    for (i = 0; i < param2.length; i++) {
        merged[k] = param2[i];
        k++;
    }
    return merged;
}

Есть ли способ избежать forпетель здесь?

Ответы (2)

Вот что я написал для этой цели.

function MergeBytes(bytes memory a, bytes memory b) public pure returns (bytes memory c) {
    // Store the length of the first array
    uint alen = a.length;
    // Store the length of BOTH arrays
    uint totallen = alen + b.length;
    // Count the loops required for array a (sets of 32 bytes)
    uint loopsa = (a.length + 31) / 32;
    // Count the loops required for array b (sets of 32 bytes)
    uint loopsb = (b.length + 31) / 32;
    assembly {
        let m := mload(0x40)
        // Load the length of both arrays to the head of the new bytes array
        mstore(m, totallen)
        // Add the contents of a to the array
        for {  let i := 0 } lt(i, loopsa) { i := add(1, i) } { mstore(add(m, mul(32, add(1, i))), mload(add(a, mul(32, add(1, i))))) }
        // Add the contents of b to the array
        for {  let i := 0 } lt(i, loopsb) { i := add(1, i) } { mstore(add(m, add(mul(32, add(1, i)), alen)), mload(add(b, mul(32, add(1, i))))) }
        mstore(0x40, add(m, add(32, totallen)))
        c := m
    }
}

Я новичок в программировании Эфириума, поэтому может быть ошибка или какие-то явные оптимизации, которые можно сделать, но я протестировал этот код в Remix. Для 2 5-байтовых массивов это стоило около 1500 газа, с 2 большими (длиной ~ 40 байт) массивами байтов это стоило около 1700 газа. Похоже, что это примерно 100 единиц газа на 32 байта.

Пожалуйста, дайте мне знать, есть ли какие-либо явные оптимизации, поскольку я использую это в своем собственном контракте!

Изменить: я внес изменение в алгоритм, так как он не работал для байтовых массивов длиной >32 байта.

Как можно найти в репозитории Solidity- Examples, предоставленном Foundation, это выглядит так:

function concat(bytes memory self, bytes memory other)
returns (bytes memory) {
      bytes memory ret = new bytes(self.length + other.length);
      var (src, srcLen) = Memory.fromBytes(self);
      var (src2, src2Len) = Memory.fromBytes(other);
      var (dest,) = Memory.fromBytes(ret);
      var dest2 = dest + srcLen;
      Memory.copy(src, dest, srcLen);
      Memory.copy(src2, dest2, src2Len);
      return ret;
}

https://github.com/ethereum/солидность-примеры

на линии var dest2 = dest + src2Len, не должно быть вместо этого var dest2 = dest + srcLen. Обратите внимание на изменение с src2Lenна srcLen?