Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro for endian-independent conversion to big endian

Tags:

c

endianness

c11

Is it possible to write a macro in C which takes an uint32_t and converts it to big endian representation no matter if the target system is little or big endian such that the macro can be evaluated at compile-time for a constant?

I found this question: endianness conversion, regardless of endianness, however, the answers only provide functions. My situation is that I would like a compile-time expression to be able to write something like this:

const uint32_t magic_number = BIGENDIAN32(0x12345678);
like image 694
nielsen Avatar asked Sep 17 '19 13:09

nielsen


2 Answers

You can use a union, which is endianess-dependent, together with bit shifts that don't depend on endianess. Run-time version:

uint32_t big_endian (uint32_t n)
{
  union
  {
    uint32_t u32;
    uint8_t  u8 [4];
  } be;

  for(size_t i=0; i<4; i++)
  {
    size_t shift = (4-1-i) * 8;
    be.u8[i] = (n >> shift) & 0xFFu;
  }
  return be.u32;
}

u8[0] will always contain the MS byte on big endian machines. However, n >> shift will grab the relevant byte portably. Notably the whole function is just overhead bloat when running on a big endian machine.

Converting this to an ugly compile-time macro would be something like this:

typedef union
{
  uint32_t u32;
  uint8_t  u8 [4];
} be_t;


#define BIG_ENDIAN(n) ( _Generic((n), uint32_t: (void)0), \
  (be_t){ .u8 = { ((n) >> 24)&0xFFu,                      \
                  ((n) >> 16)&0xFFu,                      \
                  ((n) >>  8)&0xFFu,                      \
                   (n)&0xFFu } }.u32)

The _Generic check + , operator is just for type safety and can be removed if stuck with non-standard C. The macro uses a temporary union in the form of a compound literal (outer {}), initializes the u8 array (inner {}) then returns a uint32_t value.

Trying BIG_ENDIAN(0x12345678) on little endian x86 and disassembling, I get:

mov     esi, 2018915346

2018915346 dec = 0x78563412

like image 80
Lundin Avatar answered Oct 18 '22 01:10

Lundin


Consider a compound literal of a union.

#define BIGENDIAN32(x) (((union { uint8_t  u8[4]; uint32_t u32; }) \
    { {((uint32_t)(x)>>24)&255, ((uint32_t)(x)>>16)&255, \
       ((uint32_t)(x)>> 8)&255,  (uint32_t)(x)&255} }).u32) \
//    MSbyte first                                 LSByte last

int main(void) {
  const uint32_t magic_number = BIGENDIAN32(0x12345678u);
  return (int) magic_number;
}
like image 42
2 revs Avatar answered Oct 18 '22 01:10

2 revs