Home / エンディアン変換

エンディアン変換

移植への配慮

アプリケーションばかり書いているとエンディアンが異なるマシンへの移植なんて考えないのだが、成る程と思う事有り。

次の二つは書き方の違いに過ぎないように見えるが(この例はシフトやOR演算は本質では無い)…

u_int32_t *src
u_int32_t data;
…
u_int8_t *p = (u_int8_t)&data;
…
src[0] = 0x78100000 | (p[0] << 8);
src[1] = 0x78110000 | (p[1] << 8);
src[2] = 0x78120000 | (p[2] << 8);
src[3] = 0x78130000 | (p[3] << 8);
u_int32_t *src
u_int32_t data;
…
src[0] = 0x78100000 | ((data & 0x000000ff) <<  8);
src[1] = 0x78110000 | ( data & 0x0000ff00       );
src[2] = 0x78120000 | ((data & 0x00ff0000) >>  8);
src[3] = 0x78130000 | ((data & 0xff000000) >> 16);

後者はそうではないが、前者はエンディアンが異なるマシンで異なる結果を生む。 data をバイト配列アクセスしている処が原因。

成る程…前者の方が簡潔に見えるので好まれるかも知れないが、そう言う事である。 昔書いた次の節のC++コードは失格 orz

於 Windows

先日、Windows プログラム(非ネットワークプログラム)でビッグエンディアンからリトルエンディアンへ変換する必要があった。然したる処理速度を要求されている訳でもなく、急ぎだったのでさっさと #include と #pragma comment(lib, "ws2_32.lib") を書いて ntohl/ntohs を使ったのだが、どんなコードになっているのか興味があって調べてみた。

VC6 Debug、混合モードで追い、ntohl/ntohs についてそれとなくコメントを付けてみた。

    DWORD y = ntohl(0x12345678);
/*
74F93304   mov   ecx,dword ptr [esp+4]    ; 引数 0x12345678 を cx へ
74F93308   mov   eax,ecx        ; ax へ
74F9330A   mov   edx,ecx        ; 上位ワードの作成
74F9330C   shl   eax,10h        ; 0x56780000
74F9330F   and   edx,0FF00h     ; 0x00005600
74F93315   or    eax,edx        ; 0x56785600
74F93317   mov   edx,ecx        ; 下位ワードの作成
74F93319   shr   edx,10h        ; 0x00001234
74F9331C   and   ecx,0FF0000h   ; 0x00340000
74F93322   or    edx,ecx        ; 0x00341234
74F93324   shl   eax,8          ; 0x78560000 シフトして上位ワード
74F93327   shr   edx,8          ; 0x00003412 シフトして下位ワード
74F9332A   or    eax,edx        ; 0x78563412 合成して出来上がり
74F9332C   ret   4
*/
    WORD a = ntohs(0x1234);
/*
74F91512   movzx ecx,word ptr [esp+4]
74F91517   mov   eax,ecx
74F91519   xor   edx,edx
74F9151B   shr   eax,8
74F9151E   mov   dh,cl
74F91520   or    eax,edx
74F91522   ret   4
*/

ntohs はともかく、ntohl も流石にレジスタのみで得るようになっており、 word 単位(MSでは16bitの意味)でシフトして最後に byte シフトして合成している。奇をてらわない素直な流れと思う。
意義は無いが、これをそっくり真似てインラインアセンブリ関数にするならば、こんな感じか…↓

DWORD n2hl(DWORD v)
{
    _asm {
        mov   ecx, v
        mov   eax, ecx
        mov   edx, ecx
        shl   eax, 10h
        and   edx, 0FF00h
        or    eax, edx
        mov   edx, ecx
        shr   edx, 10h
        and   ecx, 0FF0000h
        or    edx, ecx
        shl   eax, 8
        shr   edx, 8
        or    eax, edx
        mov   v,   eax
    }
    return v;
}

さて、同じ事を C++で書くと効率悪いコードになるか...

70:       DWORD x = 0x12345678;
00401458   mov   dword ptr [ebp-14h],12345678h
71:
75:       DWORD y = ((BYTE*)&x)[3]        |
76:                (((BYTE*)&x)[2]) <<  8 |
77:                (((BYTE*)&x)[1]) << 16 |
78:                (((BYTE*)&x)[0]) << 24;
0040145F   mov   eax,dword ptr [ebp-11h]   ; [3] を load
00401462   and   eax,0FFh                  ; 0x00000012
00401467   mov   ecx,dword ptr [ebp-12h]   ; [2] を load
0040146A   and   ecx,0FFh                  ; 0x00000034
00401470   shl   ecx,8                     ; 0x00003400
00401473   or    eax,ecx                   ; 0x00003412
00401475   mov   edx,dword ptr [ebp-13h]   ; 以下略
00401478   and   edx,0FFh                  ; 
0040147E   shl   edx,10h                   ; 
00401481   or    eax,edx                   ; 
00401483   mov   ecx,dword ptr [ebp-14h]   ; 
00401486   and   ecx,0FFh                  ; 
0040148C   shl   ecx,18h                   ; 
0040148F   or    eax,ecx                   ; 0x78563412
00401491   mov   dword ptr [ebp-18h],eax   ; y へ返す。

Debug 版だとそのまんまな感じだが、Release 版(最適化)にすると少し変形された↓

004010B4   mov   ecx,dword ptr [ebp+9]     ; [1] を load ... 0x??123456
004010B7   mov   edx,dword ptr [ebp+0Ah]   ; [2] を load ... 0x????1234
004010BA   xor   eax,eax                   ; 
004010BC   and   ecx,0FFh                  ; 0x00000056
004010C2   mov   ah,byte ptr [ebp+8]       ; [0] を load ... 0x00007800
004010C5   and   edx,0FFh                  ; 0x00000034
004010CB   or    eax,ecx                   ; 0x00007856
004010CD   mov   ecx,dword ptr [ebp+0Bh]   ; [3] を load ... 0x??????12
004010D0   shl   eax,8                     ; 0x00785600
004010D3   or    eax,edx                   ; 0x00785634
004010D5   and   ecx,0FFh                  ; 0x00000012
004010DB   shl   eax,8                     ; 0x78563400
004010DE   or    eax,ecx                   ; 0x78563412

ま、この程度なら C/C++ で書いても(バグが無ければ)大差無さそう…。


Home / エンディアン変換

© 2008 usskim    http://usskim.web.fc2.com/
inserted by FC2 system