C言語でバイトオーダー変換

C言語でバイトオーダー変換が必要になったのでそのときのメモ。バイトオーダーの説明については他に詳しい解説があるのでここでは省略。

コメントで @fujitanozomu さんにすっきりした書き方を指摘してもらえたため参考にして修正。

## バイトオーダー変換
やっていることはバイトごとに値を交換しているだけ。

```c
int swap_int( const int inint )
{
   int retval;
   char *conv32 = ( char* ) & inint;
   char *ret32 = ( char* ) & retval;

   // swap the bytes into a temporary buffer
   ret32[0]=conv32[3];   ret32[1]=conv32[2];
   ret32[2]=conv32[1];   ret32[3]=conv32[0];
   return retval;
}
```

様々な型に対して上記のような操作をすればいいのだが GNU Cライブラリの `byteswap.h` を使用し変換関数を定義する。

## byteswap.h による変換
`byteswap.h` には 16, 32, 64 bit のバイトオーダー変換の `bswap_16()`,`bswap_32()`,`bswap_64()` 関数があり手軽に使用できるが、整数型でしか使えない。

- 確認コード

```c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <byteswap.h>

int main(int argc, char **argv)
{
  int    ii=-10000, iic;
  uint   ui=10000,  uic;
  float  f=10.12345, fc;
  double d=-200.011, dc;
  int64_t i64=-123456, i64c;
  uint64_t ui64=1234567890123, ui64c;

  iic = bswap_32(ii);
  uic = bswap_32(ui);
  fc  = bswap_32(f);

  dc    = bswap_64(d);
  i64c  = bswap_64(i64);
  ui64c = bswap_64(ui64);
 
  // 出力処理

  return EXIT_SUCCESS;
}
```

- output
  - float, doubleの浮動小数点の変換が期待していたようにならない。
  - 返り値が整数型なので浮動小数点型に代入することによって暗黙のキャストがおきているため。

``` sh
int
f0 d8 ff ff 
ff ff d8 f0 

uint
10 27 00 00 
00 00 27 10 

float
a7 f9 21 41 
00 00 20 4d 

double
31 08 ac 1c 5a 00 69 c0 
00 00 00 00 00 80 cc 43 

int64_t
c0 1d fe ff ff ff ff ff 
ff ff ff ff ff fe 1d c0 

uint64_t
cb 04 fb 71 1f 01 00 00 
00 00 01 1f 71 fb 04 cb 
```

また、整数型でも引数と戻り値が signed, unsigned で整合性がないと期待した結果にならないので注意。

## 変換関数を定義

`byteswap.h` 関数を使い関数を定義。

- floatやdoubleに関しては暗黙のキャストが起きないように整数型にコピーをし、エンディアン変換し、再びキャストに注意し浮動小数点数型に戻す。
- 整数型に関しては `bswap_32()` や `bswap_64()` をそのまま使えばいいのだが浮動小数点数型と統一するために(若干無駄だが)関数化。
  - signed と unsigned はバイト交換部分の処理は同じだが、引数と返り値に整合性が取れていないと結果がおかしくなるので別々に定義しておく。あくまでも符号の有無で関数名をわかりやすくしているだけで引数に signed, 返り値に unsigned などを指定すると当然結果がおかしくなるのでそこは自分で注意する必要がある。
- 16bit 変数変換も同様に定義可能。

- 確認コード
  
```c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <byteswap.h>
#include <string.h>

int swap_int( const int inint )
{
   return bswap_32(inint);
}

uint swap_uint( const uint inuint )
{
   return bswap_32(inuint);
}

float swap_float( const float infloat )
{
   float retval;
   uint32_t uint32;
   memcpy(&uint32, &infloat, sizeof(infloat));
   uint32 = bswap_32(uint32);
   memcpy(&retval, &uint32, sizeof(uint32));
   return retval;
}

double swap_double( const double indouble )
{
   double retval;
   uint64_t uint64;
   memcpy(&uint64, &indouble, sizeof(indouble));
   uint64 = bswap_64(uint64);
   memcpy(&retval, &uint64, sizeof(uint64));
   return retval;
}

int64_t swap_int64_t( const int64_t inint64_t )
{
   return bswap_64(inint64_t);
}

uint64_t swap_uint64_t( const uint64_t inuint64_t )
{
   return bswap_64(inuint64_t);
}

int main(int argc, char **argv)
{
  int    ii=-10000, iic;
  uint   ui=10000, uic;
  float  f=10.12345, fc;
  double d=-200.011, dc;
  int64_t i64=-123456, i64c;
  uint64_t ui64=1234567890123, ui64c;

  iic = swap_int(ii);
  uic = swap_uint(ui);
  fc  = swap_float(f);

  dc    = swap_double(d);
  i64c  = swap_int64_t(i64);
  ui64c = swap_uint64_t(ui64);

  // 出力処理

  return EXIT_SUCCESS;
}
```

- output

```sh
int
f0 d8 ff ff 
ff ff d8 f0 

uint
10 27 00 00 
00 00 27 10 

float
a7 f9 21 41 
41 21 f9 a7 

double
31 08 ac 1c 5a 00 69 c0 
c0 69 00 5a 1c ac 08 31 

int64_t
c0 1d fe ff ff ff ff ff 
ff ff ff ff ff fe 1d c0 

uint64_t
cb 04 fb 71 1f 01 00 00 
00 00 01 1f 71 fb 04 cb 
```


## 出力処理
上記のコードの出力処理部分は byte ごとに16進数表示している

```c
  printf("int\n");
  for(int i=0; i<sizeof(int); i++)
    printf("%02x ", ((uint8_t*)(&ii))[i]);
  printf("\n");
  for(int i=0; i<sizeof(int); i++)
    printf("%02x ", ((uint8_t*)(&iic))[i]);
  printf("\n");
  printf("\n");

  printf("uint\n");
  for(int i=0; i<sizeof(uint); i++)
    printf("%02x ", ((uint8_t*)(&ui))[i]);
  printf("\n");
  for(int i=0; i<sizeof(uint); i++)
    printf("%02x ", ((uint8_t*)(&uic))[i]);
  printf("\n");
  printf("\n");

  printf("float\n");
  for(int i=0; i<sizeof(float); i++)
    printf("%02x ", ((uint8_t*)(&f))[i]);
  printf("\n");
  for(int i=0; i<sizeof(float); i++)
    printf("%02x ", ((uint8_t*)(&fc))[i]);
  printf("\n");
  printf("\n");

  printf("double\n");
  for(int i=0; i<sizeof(double); i++)
    printf("%02x ", ((uint8_t*)(&d))[i]);
  printf("\n");
  for(int i=0; i<sizeof(double); i++)
    printf("%02x ", ((uint8_t*)(&dc))[i]);
  printf("\n");
  printf("\n");

  printf("int64_t\n");
  for(int i=0; i<sizeof(int64_t); i++)
    printf("%02x ", ((uint8_t*)(&i64))[i]);
  printf("\n");
  for(int i=0; i<sizeof(int64_t); i++)
    printf("%02x ", ((uint8_t*)(&i64c))[i]);
  printf("\n");
  printf("\n");

  printf("uint64_t\n");
  for(int i=0; i<sizeof(uint64_t); i++)
    printf("%02x ", ((uint8_t*)(&ui64))[i]);
  printf("\n");
  for(int i=0; i<sizeof(uint64_t); i++)
    printf("%02x ", ((uint8_t*)(&ui64c))[i]);
  printf("\n");
```

## 参考
- https://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian