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.
var dest2 = dest + src2Len, shouldn't it be insteadvar dest2 = dest + srcLen. Notice the change fromsrc2LentosrcLen? – Marcelo Fornet May 09 '21 at 23:57