[バイトオーダー]ビックエンディアン/リトルエンディアン

![280px-Big-Endian-ja.svg.png](https://qiita-image-store.s3.amazonaws.com/0/258841/acf9f13d-f353-348c-fdc1-228dcc594864.png)

## 概要

エンディアンについて調べたことのアウトプット

データをバイト単位で配置する際のやり方の一つで二つの違いの結論だけ述べると、

ビッグエンディアン：「最初のバイトからデータを並べる」やり方
リトルエンディアン：「最後のバイトからデータを並べる」やり方

といった特性を持つ並べ方である。
詳しくは下記に記載していきます。

## エンディアンとは

プログラムを動かすときに、データは1バイト単位(=8ビット)で 記憶されていきます。
2バイト以上のデータを保存する時に、どのような順序で記憶していくか、という方法を示すのがエンディアンになります 。
エンディアンはCPUによって決まっています。

ビックエンディアン、ミドルエンディアン、リトルエンディアンといった種類がある。

ビックエンディアンとリトルエンディアンを例に挙げて説明。
long型の変数として2882400001 = 0xABCDEF01という数があるとする。
この場合それぞれの並び順序は

___ビックエンディアン： AB CD EF 01___
___リトルエンディアン： 01 EF CD AB___

ビッグエンディアンの方はデータの上の方から準に詰めていくのに対し、
リトルエンディアンは下の方から順に詰めていきます。

## エンディアンについて調べてみた

以下はC言語でエンディアンを特定するためのソース。
lscpuコマンドを使用すればわかるが今回はエンディアン変換まで行いたいので割愛。
共用体を用いてバイトオーダーを確認(参考:wiki)

``` c:endian.c
#include <stdint.h>
#include <stdio.h>

int main (int argc, char **argv) {
    union {
        uint32_t b4;
        uint16_t b2[2]
        uint8_t  b1[4]
    } bytes ;

    bytes.b4 = 0x12345678 ;
    printf ("bytes.b4: %08X\n", bytes.b4) ;
    printf ("bytes.b2: %04X, %04X\n", bytes.b2[0], bytes.b2[1]) ;
    printf ("bytes.b1: %02X, %02X, %02X, %02X\n", bytes.b1[0], bytes.b1[1], bytes.b1[2], bytes.b1[3]) ;
    return 0 ;
}
```

上記をコンパイルして実行する。
すると並び順が入れ替わっているのでリトルエンディアンであることが分かる。

``` bash
$ gcc endian.c
$ ./a.out
bytes.b4: 12345678
bytes.b2: 5678, 1234
bytes.b1: 78, 56, 34, 12
```

ちなみに検証機はwindows10のインテル系CPUなので当然である。
残念ながら自宅にはビックエンディアンでの検証ができないのでいったん締め
次はByte swappingについて調べる。

## Byte swappingとは

エンディアンを相互に変換すること、つまり、
___ビッグエンディアン⇒リトルエンディアン___
または
___リトルエンディアン⇒ビッグエンディアン___
のことをByte swappingという

エンディアン変換は以下のようなツールもあるが今回は自前で作成してみる。
http://web.save-editor.com/tool/wse_hex.html

``` byte_swap.c
#include <stdio.h>
#include <stdint.h>

#define BASENUM 0xABCD0123

void check_endian() {
        int x = 1;
        if( *(char *)&x ){
                fprintf(stdout, "little endian\n");
        }else{
                fprintf(stdout, "big endian\n");
        }
}

uint32_t byte_swap(uint32_t value) {
        uint32_t ret;
        ret  = value              << 24;
        ret |= (value&0x0000FF00) <<  8;
        ret |= (value&0x00FF0000) >>  8;
        ret |= value              >> 24;
        return ret;
}

int main(int argc, char* argv[]) {
        unsigned int value = BASENUM;

        check_endian();

        fprintf(stdout, "base : %08x\n", value);
        fprintf(stdout, "swap ; %08x\n", byte_swap(value));

        return 0;
}
```

上記の実行結果は下記のようになります。
エンディアンの特定とbyte swappingを実装してみました。

``` bash
$ ./a.out
little endian
base : abcd0123
swap : 2301cdab
```

エンディアン特定については全く思いつかずで下記を参考にしています。
大変わかりやすい記事ですので是非ご参照ください。
http://torasukenote.blog120.fc2.com/blog-entry-105.html


## プロセッサ以外のバイトオーダ

プロセッサ界隈以外でもバイトオーダーという言葉は使われることがあります。
それは「ネットワークバイトオーダ」です。
TCP/IPなどの通信プロトコルはバイト配列を扱いますが、MACやIPアドレスといったデータは多バイトデータですので、
通信路に出力する順番を決める必要があります。
ちなみにTCP/IPのネットワークバイトオーダはビッグエンディアン。

hton(3)やhtols(3)が変換にあたります。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/byteorder.3.html

| 関数 | 説明 |
|:--|:--|
| uint32_t htonl(uint32_t hostlong) | 32bitのホストバイトオーダーをネットワークバイトオーダーに変換 |
| uint16_t htons(uint16_t hostshort) | 16bitのホストバイトオーダーをネットワークバイトオーダーに変換 |
| uint32_t ntohl(uint32_t netlong) | 32bitのネットワークバイトオーダーをホストバイトオーダーに変換 |
| uint16_t ntohs(uint16_t netshort) | 16bitのネットワークバイトオーダーをホストバイトオーダーに変換 |

## ビックエンディアンはどこで

ビックエンディアンは自宅で用意できないと思ったが調べてみるとJavaの仮想マシンはビックエンディアンらしい。
他にもIBM社のメインフレームもビックエンディアンとのこと。
(参考：http://e-words.jp/w/%E3%83%93%E3%83%83%E3%82%B0%E3%82%A8%E3%83%B3%E3%83%87%E3%82%A3%E3%82%A2%E3%83%B3.html)

* JVM
* IBMメインフレームワーク
* TCP/IPのネットワークオーダー

上記3点は少なくともビックエンディアン。