Reputation: 17248
I have binary, big-endian consisting of a message like:
struct Msg
{
// 4 byte int
// 6 byte int
// 2 byte int
};
and I wish to read the 6 byte integer.
I haven't encountered a member before which wasn't 1, 2, 4 or 8 bytes before.
I'm not concerned with portability, you can assume this is on a Linux system using GCC compiler.
To read this should I do:
struct Msg
{
uint32_t a;
uint16_t b; // Part of 6 byte field
uint32_t c; // Part of 6 byte field
uint16_t d;
}
and then do I reinterpret_cast
the entire message, swap the bytes (to convert to little endian) for b
and c
and then multiply the value of b
by FFFF
before adding to c
?
Msg msg = *reinterpret_cast<Msg*>(&bytes[0]);
value = (__builtin_bswap16(msg.b) << 32) + __builtin_bswap32(msg.c);
Upvotes: 1
Views: 694
Reputation: 44258
I think it is simpler and easier to write a function which will convert int
of arbitrary size:
uint64_t readBigEndInt( const unsigned char *buff, size_t size )
{
uint64_t r = 0;
while( size-- )
r = (r << 8) + *buff++;
return r;
}
and just apply it to parts of raw buffer accordingly.
Also thanks for @MSalters if you care about speed, make this function template:
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;
}
so compiler can do better optimizations and you can provide specializations for 1,2,4 and 8 bytes if necessary. Also I would wrap it into thin class representing stream, that keeps current position and shift it by read operation. So your code at the end would be something like:
stream s(buffer,size);
uint32_t a = s.read<4>();
uint64_t b = s.read<6>();
uint16_t c = s.read<2>();
and so on.
Upvotes: 3
Reputation: 179819
By far the easiest non-portable solution would be to read the 6+2 byte members into a single std::uint64_t
, extract the lower 16 bits and byte-swap those, and then byte-swap the remaining 6 bytes (which will neatly move them down into the lower 48 bits)
Remember to zero the lower 16 bits after extracting them.
Upvotes: 1
Reputation: 67733
The only portable way to handle this is as Kamil suggests in comments, to use a char
array of the correct size, and manually extract fields from the correct offsets. You can certainly write wrapper machinery to make this nicer if you're doing this enough to make it worthwhile.
For your struct approach to work correctly, you need a non-portable #pragma pack
or __attribute__((packed))
or other compiler-specific way to prevent normal padding and alignment of the members.
Upvotes: 0