将8个十六进制字符转换为4-uint8_t数组的最有效方法?

时间:2012-12-27 14:01:48

标签: c++ integer hex

我有一个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(函数原型)将会出现问题。

3 个答案:

答案 0 :(得分:1)

您可以添加并行性,因为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个算术运算中进行。

答案 1 :(得分:0)

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

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

你所写的内容可能也足够有效。当然,您再次检查无效输入也无法获得结果。

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

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

似乎是

的替代品
( (h & 0x40) ? 10 : 0 )

我看不出我的表达方式效率低于你的表达方式,而且意图可能更清晰。 (如果你坚持使用hex,请使用0xA而不是10)

答案 2 :(得分:-2)

有几种方法可行。最简单的 最便携的是将字符分成两个字符 std::string,使用每个来初始化std::istringstream, 设置正确的格式标志,并从中读取值。 一个更有效的解决方案是创建单个 字符串,插入空格以分隔各个值, 只需使用一个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;
}