Concat matrices de dos bytes con ensamblaje

¿Alguien sabe cómo concatenar de manera eficiente matrices de dos bytes con lenguaje ensamblador para ahorrar costos de gasolina? Actualmente tengo

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

¿Hay alguna manera de evitar forbucles aquí?

Respuestas (2)

Aquí hay algo que escribí para este propósito.

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

Soy nuevo en la programación de ethereum, por lo que puede haber un error o algunas optimizaciones claras que se pueden hacer, pero probé este código en Remix. Para 2 arreglos de 5 bytes costaba alrededor de 1500 de gas, con 2 arreglos de bytes más grandes (~ 40 bytes de largo) costaba alrededor de 1700 de gas. Parece ser un aumento de aproximadamente 100 gases por cada 32 bytes.

¡Avíseme si hay optimizaciones claras ya que estoy usando esto en mi propio contrato!

Editar: hice un cambio en el algoritmo ya que no funcionó para matrices de bytes> 32 bytes de largo.

Como se puede encontrar en el repositorio solidity-examples proporcionado por Foundation, es así:

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/solidity-examples

en línea var dest2 = dest + src2Len, ¿no debería ser en su lugar var dest2 = dest + srcLen. Observe el cambio de src2Lena srcLen?