[Ethereum] Concat two bytes arrays with Assembly

assemblybytessolidity

Does anyone know how to efficiently concat two bytes arrays with assembly language to save gas costs? Currently I have

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

Is there a way to avoid for loops here?

Best Answer

Here's something I wrote for this purpose.

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

I'm new to ethereum programming, so there may be a mistake or some clear optimizations that can be made, but I tested this code in Remix. For 2 5 bytes arrays it costed about 1500 gas, with 2 larger (~ 40 bytes long) bytes arrays it costed about 1700 gas. It looks to be about a 100 gas increase per 32 bytes.

Please let me know if there are any clear optimizations as I'm using this in my own contract!

Edit: I made a change in the algorithm as it didn't work for byte arrays >32 bytes long.