SBKarr SBKarr - 5 months ago 10
Android Question

How to read numeric from binary data, crossplatform (C/C++)?

I have raw binary data blocks (actually,

CBOR
-encoded). To read numeric I use common form like:

template <typename T> // T can be uint64_t, double, uint32_t, etc...
auto read(const uint8_t *ptr) -> T {
return *((T *)(ptr));
}


This solution works on
x86/x86_64
PC and
arm/arm64
iOS.
But, on
arm/armv7
Android with
clang
compiler on default release optimization level (
-Os
) i receive
SIGBUS
with code
1
(unaligned read) for types, larger then one byte. I fix that problem with another solution:

template <typename T>
auto read(const uint8_t *ptr) -> T {
union {
uint8_t buf[sizeof(T)];
T value;
} u;
memcpy(u.buf, ptr, sizeof(T));
return u.value;
}


Is there any platform-independent solution, that will not impact performance?

Answer

The only platform-independent and correct way is to use memcpy. You don't need a union.

Don't worry about efficiency. memcpy is a magic function, and the compiler will 'do the right thing'.

example when compiled for x86:

#include <cstring>
#include <cstdint>

template <typename T>
auto read(const uint8_t *ptr) -> T {
  T result;
  std::memcpy(&result, ptr, sizeof(T));
    return result;
}

extern const uint8_t* get_bytes();
extern void emit(std::uint64_t);

int main()
{
  auto x = read<std::uint64_t>(get_bytes());
  emit(x);

}

yields assembler:

main:
        subq    $8, %rsp
        call    get_bytes()
        movq    (%rax), %rdi         ; note - memcpy utterly elided
        call    emit(unsigned long)
        xorl    %eax, %eax
        addq    $8, %rsp
        ret
Comments