Search code examples
bit-manipulationsolidityrsk

Convert uint24 to HEX string in Solidity


I am trying to convert uint24 value of a kind 0x00ff08 to a human readable string with the same characters in Solidity smart contract which I am going to deploy on RSK. In my question about bytes3 to hex string casting I was advised for these purposes to use a function

function uint24tohexstr(uint24 i) public pure returns (string memory) {
        bytes memory o = new bytes(6);
        uint24 mask = 0x00000f;
        o[5] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[4] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[3] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[2] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[1] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[0] = bytes1(uint8tohexchar(uint8(i & mask)));
        return string(o);
    }

I wanted to make use of a loop in that function and rewrote it like this

function uint24ToHexStr(uint24 i) public pure returns (string memory) {
        bytes memory o = new bytes(6);
        uint24 mask = 0x00000f; // hex 15
        for(uint k = 5; k >= 0; k -= 1) {
          o[k] = bytes1(uint8ToHexCharCode(uint8(i & mask)));
          i >>= 4;
        }
        return string(o);
    }

But unfortunately this function causes runtime error because on the last iteration unsigned integer k becomes -1. The first thing that crossed my mind was to increase k by one so that

        for(uint k = 6; k >= 1; k -= 1) {
          o[k - 1] = bytes1(uint8ToHexCharCode(uint8(i & mask)));
        }

Can anyone think of a more elegant way to achieve the same result?


Solution

  • I like the 2nd option in @selbie's answer; and thought what if we used a different type of loop control structure. A regular while loop would inherit the same "final iteration uint underflow" problem that the for loop has. Switching to a do .. while loop, on the other hand, allows you to shift the evaluation of the condition from being checked before the iteration, to being checked after the iteration. This can be applied to your implementation like so:

        function uint24ToHexStr(uint24 i) public pure returns (string memory) {
            bytes memory o = new bytes(6);
            uint24 mask = 0x00000f; // hex 15
            uint k = 6;
            do {
                k--;
                o[k] = bytes1(uint8ToHexCharCode(uint8(i & mask)));
                i >>= 4;
            } while (k > 0);
            return string(o);
        }
    

    This avoids both the uint underflow, and also does not require k - 1 for array indices within the loop. In terms of gas cost, I would "guesstimate" that it would be close to the original implementation of the same function from the previous question. (but actually try both out and compare to confirm)