Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you write (portably) reverse network byte order?

Background

When designing binary file formats, it's generally recommended to write integers in network byte order. For that, there are macros like htonhl(). But for a format such as WAV, actually the little endian format is used.

Question

How do you portably write little endian values, regardless of if the CPU your code runs on is a big endian or little endian architecture? (Ideas: can the standard macros ntohl() and htonl() be used "in reverse" somehow? Or should the code just test runtime if it's running on a little or big endian CPU and choose the appropriate code path?)

So the question is not really about file formats, file formats were just an example. It could be any kind of serialization where little endian "on the wire" is required, such as a (heretic) network protocol.

like image 679
Prof. Falken Avatar asked Apr 15 '13 06:04

Prof. Falken


1 Answers

Here's a template based version:

#include <iostream>
#include <iomanip>

enum endianness_t {
   BIG,         // 0x44332211  => 0x44 0x33 0x22 0x11
   LITTLE,      // 0x44332211  => 0x11 0x22 0x33 0x44
  UNKNOWN
};

const uint32_t test_value    = 0x44332211;
const bool is_little_endian  = (((char *)&test_value)[0] == 0x11) && (((char *)&test_value)[1] == 0x22);
const bool is_big_endian     = (((char *)&test_value)[0] == 0x44) && (((char *)&test_value)[1] == 0x33);

const endianness_t endianness = 
   is_big_endian ? BIG: 
  (is_little_endian ? LITTLE : UNKNOWN);


template <typename T>
T identity(T v){
  return v;
}

// 16 bits values ------

uint16_t swap_(uint16_t v){
  return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
}

// 32 bits values ------

uint32_t swap_(uint32_t v){
  return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24);
}

template <typename T, endianness_t HOST, endianness_t REMOTE>
 struct en_swap{
  static T conv(T v){
    return swap_(v);
  }
};

template <typename T>
struct en_swap<T, BIG, BIG>{
  static T conv(T v){
    return v;
  }
};

template <typename T>
struct en_swap<T, LITTLE, LITTLE> {
  static T conv(T v){
    return v;
  }
};

template <typename T>
T to_big(T v) {

  switch (endianness){
  case LITTLE :
    return en_swap<T,LITTLE,BIG>::conv(v);
  case BIG :
    return en_swap<T,BIG,BIG>::conv(v);
  }
}

template <typename T>
T to_little(T v) {
   switch (endianness){
   case LITTLE :
     return en_swap<T,LITTLE,LITTLE>::conv(v);
   case BIG :
     return en_swap<T,BIG,LITTLE>::conv(v);
  }
}


int main(){

  using namespace std;

  uint32_t x = 0x0ABCDEF0;
  uint32_t y = to_big(x);
  uint32_t z = to_little(x);

  cout << hex << setw(8) << setfill('0') << x << " " << y << " " << setw(8) << setfill('0') << z << endl;

}
like image 158
didierc Avatar answered Nov 12 '22 07:11

didierc