读取一个6字节的大端二进制字段,表示一个整数

时间:2018-08-02 12:43:06

标签: c++

我有二进制的,大尾数形式的消息,例如:

struct Msg
{
    // 4 byte int
    // 6 byte int
    // 2 byte int
};

我希望读取6个字节的整数。

我之前没有遇到过一个不是1、2、4或8个字节的成员。

我不关心可移植性,您可以假定这是在使用GCC编译器的Linux系统上。

要阅读此书,我应该这样做:

struct Msg
{
    uint32_t a;
    uint16_t b;   // Part of 6 byte field
    uint32_t c;   // Part of 6 byte field
    uint16_t d;
}

然后我reinterpret_cast整条消息,将bc的字节交换(转换为小尾数),然后将b的值乘以将FFFF添加到c之前?

Msg msg = *reinterpret_cast<Msg*>(&bytes[0]);
value = (__builtin_bswap16(msg.b) << 32) + __builtin_bswap32(msg.c);

3 个答案:

答案 0 :(得分:3)

我认为编写一个可以转换任意大小的int的函数更加简单和容易:

uint64_t readBigEndInt( const unsigned char *buff, size_t size )
{
    uint64_t r = 0;
    while( size-- )
        r = (r << 8) + *buff++;
    return r;
}

,然后将其相应地应用于原始缓冲区的一部分。

还要感谢@MSalters,如果您关心速度,请制作此功能模板:

template<size_t size>
uint64_t readBigEndInt( const unsigned char *buff )
{
    uint64_t r = 0;
    for( size_t i = 0; i < size; ++i )
        r = (r << 8) + *buff++;
    return r;
}

因此编译器可以进行更好的优化,并且可以在必要时为1,2,4和8个字节提供特殊化。另外,我会将其包装到表示流的瘦类中,该类保留当前位置并通过读取操作将其移动。因此,您的代码结尾将类似于:

stream s(buffer,size);
uint32_t a = s.read<4>();
uint64_t b = s.read<6>();
uint16_t c = s.read<2>();

以此类推。

答案 1 :(得分:1)

到目前为止,最简单的不可移植的解决方案是将6 + 2个字节成员读入单个std::uint64_t中,提取低16位并将其字节交换,然后对其余6个字节进行字节交换(它将整齐地向下移动到低48位)

记住在提取低16位后将其清零。

答案 2 :(得分:0)

唯一的便携式处理方式如Kamil在评论中所建议的那样,使用正确大小的char数组,并从正确的偏移量中手动提取字段。如果您做得足够值得,肯定可以编写包装器机制,以使其变得更好。

要使结构方法正确工作,您需要一种不可移植的#pragma pack__attribute__((packed))或其他特定于编译器的方法,以防止成员的正常填充和对齐。