2

I have an array of bytes32 with various pieces of information at various byte positions in it, and 2nd 3 bytes are a number, which I convert to uint24 like so (where currentData is of type bytes32):

uint24 convertedNumber = uint24(bytes3(currentData << 24));

However the operation happens in a loop, so the cost of shifting, then creating a bytes3, then converting to uint24 can add up as the size of the input array grows. Is there a more gas-efficient way to convert 3 bytes at a position inside a bytes32 into uint24?

3
  • So you're essentially looking for a method which is equivalent to uint24(bytes3(currentData << 24)) but cheaper? I doubt that there is one, but regardless of that, the rest of your code has nothing to do with the problem at hand, so I recommend that you get rid of it and leave only that one piece of code. It would make your question more focused on the actual problem, thus more inviting for others to read through and come up with alternative suggestions. – goodvibration Feb 17 '20 at 1:31
  • Yes thats correct, I am enquiring if anyone has a cheaper solution as the code the code i have runs in a loop, and the creation of temporary objects for this operation can add up quickly. I've cleaned up the needless code as requested. – AndroidNoob Feb 17 '20 at 9:47
  • 1
    Disagree that the rest of the code has nothing to do with the problem. I'm sure it would be helpful for others to see how the loop is used and whether there are improvements that can be made outside of the loop to achieve OP's goal of reducing gas costs. – Nick Feb 20 '20 at 17:40
1
+50

I guess there is not much to get since not many operations are included and the operations are very fundamental (shift, assign). I created a solution that can save 28 gas per conversion though:

pragma solidity ^0.6.3;

contract Test {
    function b32ToU24(bytes32 barray) external pure returns (uint24) {
        return uint24(bytes3(barray << 24));
    }

    function b32ToU24ASM(bytes32 barray) external pure returns (uint24) {
        uint24 result;

        assembly {
            // Right shift barray by 26 bytes, assign remaining 6 bytes to result
            // Since result is an uint24, the 3 msB will not be regarded
            result := shr(0xd0, barray)
        }

        return result;
    }
}

Using the solc compiler 0.6.3 with optimizations, function b32ToU24 requires 258 gas whereas function b32ToU24ASM requires 230 gas. Without optimization 327 gas and 309 gas respectively. Depending on the context there might be more margin for optimizations.

Edit1: Since you have an array of bytes32 values, you have to adapt the solution in a way that iterates through the array and loads the data (using mload) before. If you want to store the values in an array of uint24 you can use mstore with an adequate offset. (assuming everything happens in memory)

2
  • Thanks for the answer! The uint24 is used as a local variable for other calculations, when the loop comes back round it's discarded/reassigned to the next bytes32 to be processed – AndroidNoob Feb 20 '20 at 22:31
  • @AndroidNoob In that case you can use the assembly code I supplied to convert the bytes32 data to the uint24 number within your loop. There might be ways to optimize your logic within your loop without using assembly. If you want to further optimize the code to it's fullest by using assembly, you'd have to convert the logic within the loop to assembly code as well. This would enable to combine the memory load and the shift into one operation for example. Using assembly code when it is not required is disencouraged though. – sea212 Feb 21 '20 at 13:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.