1

我有一个const char*, 指向一个包含 8 个字符的数组(可能是较大字符串的一部分),其中包含一个十六进制值。我需要一个将这些字符转换为 4 数组的函数uint8_t,其中源数组中的前两个字符将成为目标数组中的第一个元素,依此类推。例如,如果我有这个

const char* s = "FA0BD6E4";

我希望它转换为

uint8_t i[4] = {0xFA, 0x0B, 0xD6, 0xE4};

目前,我有这些功能:

inline constexpr uint8_t HexChar2UInt8(char h) noexcept
{
    return static_cast<uint8_t>((h & 0xF) + (((h & 0x40) >> 3) | ((h & 0x40) >> 6)));
}

inline constexpr uint8_t HexChars2UInt8(char h0, char h1) noexcept
{
    return (HexChar2UInt8(h0) << 4) | HexChar2UInt8(h1);
}

inline constexpr std::array<uint8_t, 4> HexStr2UInt8(const char* in) noexcept
{
    return {{
        HexChars2UInt8(in[0], in[1]),
        HexChars2UInt8(in[2], in[3]),
        HexChars2UInt8(in[4], in[5]),
        HexChars2UInt8(in[6], in[7])
    }};
}

这是我从哪里调用它的样子:

const char* s = ...; // the source string
std::array<uint8_t, 4> a; // I need to place the resulting value in this array
a = HexStr2UInt8(s); // the function call does not have to look like this

我想知道,有没有更有效(和便携)的方式来做到这一点?例如,返回是std::array一件好事,还是应该将dst指针传递给HexChars2UInt8? 或者还有其他方法可以改善我的功能吗?

我问这个的主要原因是因为我可能需要在某个时候优化它,如果将来更改 API(函数原型)会出现问题。

4

3 回答 3

2

您可以添加并行性,因为 HexChar2Uint8 可以同时访问 8 个字符。一次加载未对齐的 64 位值可能比加载 8 个字符更快(并调用转换函数)

hexChar2Uints(uint8_t *ptr, uint64_t *result)  // make result aligned to qword
{
  uint64_t d=*(uint64_t*)ptr;
  uint64_t hi = (d>>6) & 0x0101010101010101;
  d &= 0x0f0f0f0f0f0f0f0f;
  *result = d+(hi*9);  // let compiler decide the fastest method
}

最后阶段必须按照 OP 的建议完成,只需从修改后的“字符串”中读取:

for (n=0;n<4;n++) arr[n]=(tmp[2*n]<<4) | tmp[2*n+1];

可以大大加快速度的可能性很小。该<< 4操作也可以注入到 hexChar2Uints 使其并行,但我怀疑它可以在少于 4 次算术运算中完成。

于 2012-12-27T14:49:49.493 回答
0

最有效的,即最快的转换方法可能是为每对可能的 2 个字符设置一个包含 65536 个值的表,并将它们的转换存储在有效的值中。

如果您将它们存储为无符号字符,您将无法捕获错误,因此您只需要希望获得有效的输入。如果您将值类型存储为大于 unsigned char ,您将能够使用某种“错误”值,但检查是否得到一个将是开销。(额外的 65536 字节可能不是)。

不过,您所写的内容可能也足够有效。当然,您也不会再检查无效输入,并且无论如何都会得到结果。

如果你保留你的,我可能会改变:

((h & 0x40) >> 3) | ((h & 0x40) >> 6)

这似乎可以替代

( (h & 0x40) ? 10 : 0 )

我看不出我的表达效率如何不如您的表达,并且意图可能更清晰。(0xA如果您坚持使用十六进制,请使用而不是 10)

于 2012-12-27T14:20:06.380 回答
-2

有几种可能的方法。最简单和最便携的是将字符分解为两个 character std::string,使用每个来初始化一个std::istringstream,设置正确的格式标志,并从中读取值。一个更有效的解决方案是创建一个字符串,插入空格来分隔各个值,然后只使用 one std::istringstream,例如:

std::vector<uint8_t>
convert4UChars( std::string const& in )
{
    assert( in.size() >= 8 );
    std::string tmp( in.begin(), in.begin() + 8 );
    int i = tmp.size();
    while ( i > 2 ) {
        i -= 2;
        tmp.insert( i, 1, ' ');
    }
    std::istringstream s(tmp);
    s.setf( std::ios_base::hex, std::ios_base::basefield );
    std::vector<int> results( 4 );
    s >> results[0] >> results[1] >> results[2] >> results[3];
    if ( !s ) {
        //  error...
    }
    return std::vector<uint8_t>( results.begin(), results.end() );
}

如果您真的想手动完成,另一种方法是创建一个 256 条目表,按每个字符索引,然后使用它:

class HexValueTable
{
    std::array<uint_t, 256> myValues;
public:
    HexValueTable()
    {
        std::fill( myValues.begin(), myValues.end(), -1 );
        for ( int i = '0'; i <= '9'; ++ i ) {
            myValues[ i ] = i - '0';
        }
        for ( int i = 'a'; i <= 'f'; ++ i ) {
            myValues[ i ] = i - 'a' + 10;
        }
        for ( int i = 'A'; i <= 'A'; ++ i ) {
            myValues[ i ] = i - 'a' + 10;
        }
    }
    uint8_t operator[]( char ch ) const
    {
        uint8_t results = myValues[static_cast<unsigned char>( ch )];
        if ( results == static_cast<unsigned char>( -1 ) ) {
            //  error, throw some exceptions...
        }
        return results;
    }
};

std::array<uint8_t, 4>
convert4UChars( std::string const& in )
{
    static HexValueTable const hexValues;
    assert( in.size() >= 8 );
    std::array<uint8_t, 4> results;
    std::string::const_iterator source = in.begin();
    for ( int i = 0; i < 4; ++ i ) {
        results[i] = (hexValues[*source ++]) << 4;
        results[i] |= hexValues[*source ++];
    }
    return results;
}
于 2012-12-27T14:41:41.390 回答