2

我正在尝试将一种类型的 uint24 值转换为具有相同字符0x00ff08的人类可读string的 Solidity 智能合约,我将在 RSK 上部署该合约。在我关于bytes3 到十六进制字符串转换的问题中,我被建议出于这些目的使用一个函数

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

我想在该函数中使用一个循环并像这样重写它

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

但不幸的是,这个函数会导致运行时错误,因为在最后一次迭代中,无符号整数k变为-1. 我首先想到的是将 k 加一,这样

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

谁能想到一种更优雅的方式来达到同样的效果?

4

3 回答 3

3

这一行:

for(uint k = 5; k >= 0; k -= 1) {

如果k是 type uint,什么k >= 0时候评估为假?(提示:从不)

k从类型更改uintint,问题就解决了。

    for(int k = 5; k >= 0; k -= 1) {
      o[k] = bytes1(uint8ToHexCharCode(uint8(i & mask)));
      i >>= 4;
    }

或者

    for(uint k = 0; k< 6; k += 1) {
      o[5-k] = bytes1(uint8ToHexCharCode(uint8(i & mask)));
      i >>= 4;
    }
于 2021-09-25T18:16:13.803 回答
2

我喜欢@selbie 回答中的第二个选项;并想如果我们使用不同类型的循环控制结构会怎样。常规while循环将继承与循环相同的“最终迭代uint下溢”问题fordo .. while另一方面,切换到循环允许您将条件评估从在迭代之前检查转移到在迭代之后检查。这可以像这样应用于您的实现:

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

这既避免了uint下溢,也不需要k - 1循环内的数组索引。在 gas 成本方面,我会“猜测”它会接近上一个问题中相同功能的原始实现。(但实际上都尝试并比较确认)

于 2021-09-26T03:47:37.000 回答
2

替代答案,也从@selbie 的前向迭代答案中获得一些灵感,但不是颠倒索引顺序,而是通过从按位和掩码切换到 5 个半字节(20 位)。像这样:

   function uint24ToHexStrAlt2(uint24 i) public pure returns (string memory) {
        bytes memory o = new bytes(6);
        uint k = 0;
        do {
            o[k] = bytes1(uint8ToHexCharCode(uint8(i >> 20))); // shift by 5 nibbles instead of mask
            i <<= 4;
            k +=1;
        } while (k < 6);
        return string(o);
    }
于 2021-09-26T04:30:07.740 回答