web-dev-qa-db-ja.com

C ++の64ビットntohl()?

htonl()のmanページは、32ビット値までしか使用できないことを示唆しているようです。 (実際には、ntohl()は、私のプラットフォームでは32ビットであるunsigned longに対して定義されています。unsignedlongが8バイトであれば、64ビットintで機能すると思います)。

私の問題は、64ビット整数(私の場合、これは符号なしlong long)をビッグエンディアンからリトルエンディアンに変換する必要があることです。今、私はその特定の変換を行う必要があります。ただし、ターゲットプラットフォームがビッグエンディアンの場合、関数(ntohl()など)が私の64ビット値を変換しないとさらに良いでしょう。 (これを行うために独自のプリプロセッサマジックを追加するのは避けたいです)。

何が使えますか?存在する場合は標準的なものが欲しいのですが、実装の提案は受け付けています。過去にこの種の変換がユニオンを使用して行われたのを見てきました。符号なしのlong longとchar [8]を持つユニオンを持つことができると思います。次に、それに応じてバイトを交換します。 (明らかに、ビッグエンディアンのプラットフォームでは動作しません)。

58
Tom

ドキュメンテーション: man htobe64 Linux(glibc> = 2.9)またはFreeBSDで。

残念ながら、OpenBSD、FreeBSD、およびglibc(Linux)は、2009年の試行中に、このための1つの(非カーネルAPI)libc標準を作成するためにスムーズに連携しませんでした。

現在、この短いプリプロセッサコードは次のとおりです。

#if defined(__linux__)
#  include <endian.h>
#Elif defined(__FreeBSD__) || defined(__NetBSD__)
#  include <sys/endian.h>
#Elif defined(__OpenBSD__)
#  include <sys/types.h>
#  define be16toh(x) betoh16(x)
#  define be32toh(x) betoh32(x)
#  define be64toh(x) betoh64(x)
#endif

(LinuxおよびOpenBSDでテスト済み)は、違いを隠す必要があります。これらの4つのプラットフォームでLinux/FreeBSDスタイルのマクロを提供します。

使用例:

  #include <stdint.h>    // For 'uint64_t'

  uint64_t  Host_int = 123;
  uint64_t  big_endian;

  big_endian = htobe64( Host_int );
  Host_int = be64toh( big_endian );

これは、現時点で利用可能な最も「標準的なCライブラリ」のようなアプローチです。

53

これを読むことをお勧めします: http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

uint64_t
ntoh64(const uint64_t *input)
{
    uint64_t rval;
    uint8_t *data = (uint8_t *)&rval;

    data[0] = *input >> 56;
    data[1] = *input >> 48;
    data[2] = *input >> 40;
    data[3] = *input >> 32;
    data[4] = *input >> 24;
    data[5] = *input >> 16;
    data[6] = *input >> 8;
    data[7] = *input >> 0;

    return rval;
}

uint64_t
hton64(const uint64_t *input)
{
    return (ntoh64(input));
}

int
main(void)
{
    uint64_t ull;

    ull = 1;
    printf("%"PRIu64"\n", ull);

    ull = ntoh64(&ull);
    printf("%"PRIu64"\n", ull);

    ull = hton64(&ull);
    printf("%"PRIu64"\n", ull);

    return 0;
}

次の出力が表示されます。

1
72057594037927936
1

上位4バイトをドロップする場合、ntohl()でこれをテストできます。

また、これを任意のサイズの整数で動作するC++のニーステンプレート関数に変換できます。

template <typename T>
static inline T
hton_any(const T &input)
{
    T output(0);
    const std::size_t size = sizeof(input);
    uint8_t *data = reinterpret_cast<uint8_t *>(&output);

    for (std::size_t i = 0; i < size; i++) {
        data[i] = input >> ((size - i - 1) * 8);
    }

    return output;
}

今、あなたの128ビットも安全です!

17
user442585

エンディアンを検出するには、次のユニオンを使用します。

_union {
    unsigned long long ull;
    char c[8];
} x;
x.ull = 0x0123456789abcdef; // may need special suffix for ULL.
_

その後、_x.c[]_の内容を確認して、各バイトがどこに行ったかを検出できます。

変換を行うには、その検出コードを一度使用して、プラットフォームが使用しているエンディアンを確認し、スワップを実行する独自の関数を作成します。

コードを任意のプラットフォームで実行するように動的にすることができます(一度検出してから、変換コード内でスイッチを使用して正しい変換を選択します)が、1つのプラットフォームのみを使用する場合は、別のプログラムで一度検出すると、単純な変換ルーチンをコーディングし、そのプラットフォームでのみ実行される(またはテストされた)ことを文書化します。

これを説明するためにホイップしたサンプルコードを次に示します。ただし、徹底的なテストは行われていませんが、開始するには十分なはずです。

_#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TYP_INIT 0
#define TYP_SMLE 1
#define TYP_BIGE 2

static unsigned long long cvt(unsigned long long src) {
    static int typ = TYP_INIT;
    unsigned char c;
    union {
        unsigned long long ull;
        unsigned char c[8];
    } x;

    if (typ == TYP_INIT) {
        x.ull = 0x01;
        typ = (x.c[7] == 0x01) ? TYP_BIGE : TYP_SMLE;
    }

    if (typ == TYP_SMLE)
        return src;

    x.ull = src;
    c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c;
    c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c;
    c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c;
    c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c;
    return x.ull;
}

int main (void) {
    unsigned long long ull = 1;
    ull = cvt (ull);
    printf ("%llu\n",ull);
    return 0;
}
_

これは純粋なビッグ/リトルエンディアンをチェックするだけであることに注意してください。たとえば、バイトが{5,2,3,1,0,7,6,4}の順序で格納されている変なバリアントがある場合、cvt()は少し複雑になります。このようなアーキテクチャは存在するに値しませんが、マイクロプロセッサ業界の友人たちの狂気を軽視しているわけではありません:-)

また、これは技術的に未定義の動作であることに注意してください。これは、最後に書き込まれたフィールド以外のフィールドからはユニオンメンバーにアクセスすることは想定されていないためです。おそらくほとんどの実装で動作しますが、純粋な観点からは、弾丸を噛んでマクロを使用して独自のルーチンを定義する必要があります。

_// Assumes 64-bit unsigned long long.
unsigned long long switchOrderFn (unsigned long long in) {
    in  = (in && 0xff00000000000000ULL) >> 56
        | (in && 0x00ff000000000000ULL) >> 40
        | (in && 0x0000ff0000000000ULL) >> 24
        | (in && 0x000000ff00000000ULL) >> 8
        | (in && 0x00000000ff000000ULL) << 8
        | (in && 0x0000000000ff0000ULL) << 24
        | (in && 0x000000000000ff00ULL) << 40
        | (in && 0x00000000000000ffULL) << 56;
    return in;
}
#ifdef ULONG_IS_NET_ORDER
    #define switchOrder(n) (n)
#else
    #define switchOrder(n) switchOrderFn(n)
#endif
_
14
paxdiablo

一部のBSDシステムにはbetoh64必要なことを行います。

12
Francis

素早い回答

_#include <endian.h>    // __BYTE_ORDER __LITTLE_ENDIAN
#include <byteswap.h>  // bswap_64()

uint64_t value = 0x1122334455667788;

#if __BYTE_ORDER == __LITTLE_ENDIAN
value = bswap_64(value);  // Compiler builtin GCC/Clang
#endif
_

ヘッダーファイル

zhaorufei (彼女/彼のコメントを参照)で報告されているように、_endian.h_はC++標準ヘッダーではなく、マクロ___BYTE_ORDER_および___LITTLE_ENDIAN_は未定義の場合があります。したがって、未定義マクロは_#if_として扱われるため、_0_ステートメントは予測できません。

エンディアンを検出するためのC++のエレガントなトリックを共有したい場合は、この回答を編集してください。

移植性

さらに、マクロbswap_64()は、GCCおよびClangコンパイラで使用できますが、Visual C++コンパイラでは使用できません。移植可能なソースコードを提供するために、次のスニペットに触発される場合があります。

_#ifdef _MSC_VER
  #include <stdlib.h>
  #define bswap_16(x) _byteswap_ushort(x)
  #define bswap_32(x) _byteswap_ulong(x)
  #define bswap_64(x) _byteswap_uint64(x)
#else
  #include <byteswap.h>  // bswap_16 bswap_32 bswap_64
#endif
_

より移植性の高いソースコードも参照してください: Cross-platform __byteswap_uint64_

C++ 14 constexprテンプレート関数

16ビット、32ビット、64ビットなどの汎用hton()...

_#include <endian.h>   // __BYTE_ORDER __LITTLE_ENDIAN
#include <algorithm>  // std::reverse()

template <typename T>
constexpr T htonT (T value) noexcept
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
  char* ptr = reinterpret_cast<char*>(&value);
  std::reverse(ptr, ptr + sizeof(T));
#endif
  return value;
}
_

C++ 11 constexprテンプレート関数

  • C++ 11では、constexpr関数でローカル変数を使用できません。
    したがって、トリックはデフォルト値で引数を使用することです。
  • さらに、C++ 11 constexpr関数には、1つの式が含まれている必要があります。
    したがって、本文は、コンマで区切られたステートメントを持つ1つのリターンで構成されます。
_template <typename T>
constexpr T htonT (T value, char* ptr=0) noexcept
{
  return 
#if __BYTE_ORDER == __LITTLE_ENDIAN
    ptr = reinterpret_cast<char*>(&value), 
    std::reverse(ptr, ptr + sizeof(T)),
#endif
    value;
}
_

_-Wall -Wextra -pedantic_を使用したclang-3.5とGCC-4.9の両方でコンパイル警告はありません
(コンパイルを参照し、 colir で出力を実行します)。

C++ 11 constexprテンプレートSFINAE関数

ただし、上記のバージョンでは、次のようにconstexpr変数を作成できません。

_constexpr int32_t hton_six = htonT( int32_t(6) );
_

最後に、16/32/64ビットに応じて機能を分離(特殊化)する必要があります。
しかし、まだ汎用関数を保持できます。
colir の完全なスニペットを参照)

以下のC++ 11スニペットでは、 traits_std::enable_if_ を使用して 代替エラーはエラーではありません (SFINAE)を利用しています。

_template <typename T>
constexpr typename std::enable_if<sizeof(T) == 2, T>::type
htonT (T value) noexcept
{
   return  ((value & 0x00FF) << 8)
         | ((value & 0xFF00) >> 8);
}

template <typename T>
constexpr typename std::enable_if<sizeof(T) == 4, T>::type
htonT (T value) noexcept
{
   return  ((value & 0x000000FF) << 24)
         | ((value & 0x0000FF00) <<  8)
         | ((value & 0x00FF0000) >>  8)
         | ((value & 0xFF000000) >> 24);
}

template <typename T>
constexpr typename std::enable_if<sizeof(T) == 8, T>::type
htonT (T value) noexcept
{
   return  ((value & 0xFF00000000000000ull) >> 56)
         | ((value & 0x00FF000000000000ull) >> 40)
         | ((value & 0x0000FF0000000000ull) >> 24)
         | ((value & 0x000000FF00000000ull) >>  8)
         | ((value & 0x00000000FF000000ull) <<  8)
         | ((value & 0x0000000000FF0000ull) << 24)
         | ((value & 0x000000000000FF00ull) << 40)
         | ((value & 0x00000000000000FFull) << 56);
}
_

または、組み込みコンパイラマクロと_std::enable_if_t<xxx>_のショートカットとしてのC++ 14構文_std::enable_if<xxx>::type_に基づくさらに短いバージョン:

_template <typename T>
constexpr typename std::enable_if_t<sizeof(T) == 2, T>
htonT (T value) noexcept
{
    return bswap_16(value);  // __bswap_constant_16
}

template <typename T>
constexpr typename std::enable_if_t<sizeof(T) == 4, T>
htonT (T value) noexcept
{
    return bswap_32(value);  // __bswap_constant_32
}

template <typename T>
constexpr typename std::enable_if_t<sizeof(T) == 8, T>
htonT (T value) noexcept
{
    return bswap_64(value);  // __bswap_constant_64
}
_

最初のバージョンのテストコード

_std::uint8_t uc = 'B';                  std::cout <<std::setw(16)<< uc <<'\n';
uc = htonT( uc );                       std::cout <<std::setw(16)<< uc <<'\n';

std::uint16_t us = 0x1122;              std::cout <<std::setw(16)<< us <<'\n';
us = htonT( us );                       std::cout <<std::setw(16)<< us <<'\n';

std::uint32_t ul = 0x11223344;          std::cout <<std::setw(16)<< ul <<'\n';
ul = htonT( ul );                       std::cout <<std::setw(16)<< ul <<'\n';

std::uint64_t uL = 0x1122334455667788; std::cout <<std::setw(16)<< uL <<'\n';
uL = htonT( uL );                      std::cout <<std::setw(16)<< uL <<'\n';
_

2番目のバージョンのテストコード

_constexpr uint8_t  a1 = 'B';               std::cout<<std::setw(16)<<a1<<'\n';
constexpr auto     b1 = htonT(a1);         std::cout<<std::setw(16)<<b1<<'\n';

constexpr uint16_t a2 = 0x1122;            std::cout<<std::setw(16)<<a2<<'\n';
constexpr auto     b2 = htonT(a2);         std::cout<<std::setw(16)<<b2<<'\n';

constexpr uint32_t a4 = 0x11223344;        std::cout<<std::setw(16)<<a4<<'\n';
constexpr auto     b4 = htonT(a4);         std::cout<<std::setw(16)<<b4<<'\n';

constexpr uint64_t a8 = 0x1122334455667788;std::cout<<std::setw(16)<<a8<<'\n';
constexpr auto     b8 = htonT(a8);         std::cout<<std::setw(16)<<b8<<'\n';
_

出力

_               B
               B
            1122
            2211
        11223344
        44332211
1122334455667788
8877665544332211
_

コード生成

オンラインC++コンパイラ gcc.godbolt.org は、生成されたコードを示します。

_g++-4.9.2 -std=c++14 -O3_

_std::enable_if<(sizeof (unsigned char))==(1), unsigned char>::type htonT<unsigned char>(unsigned char):
    movl    %edi, %eax
    ret
std::enable_if<(sizeof (unsigned short))==(2), unsigned short>::type htonT<unsigned short>(unsigned short):
    movl    %edi, %eax
    rolw    $8, %ax
    ret
std::enable_if<(sizeof (unsigned int))==(4), unsigned int>::type htonT<unsigned int>(unsigned int):
    movl    %edi, %eax
    bswap   %eax
    ret
std::enable_if<(sizeof (unsigned long))==(8), unsigned long>::type htonT<unsigned long>(unsigned long):
    movq    %rdi, %rax
    bswap   %rax
    ret
_

_clang++-3.5.1 -std=c++14 -O3_

_std::enable_if<(sizeof (unsigned char))==(1), unsigned char>::type htonT<unsigned char>(unsigned char): # @std::enable_if<(sizeof (unsigned char))==(1), unsigned char>::type htonT<unsigned char>(unsigned char)
    movl    %edi, %eax
    retq

std::enable_if<(sizeof (unsigned short))==(2), unsigned short>::type htonT<unsigned short>(unsigned short): # @std::enable_if<(sizeof (unsigned short))==(2), unsigned short>::type htonT<unsigned short>(unsigned short)
    rolw    $8, %di
    movzwl  %di, %eax
    retq

std::enable_if<(sizeof (unsigned int))==(4), unsigned int>::type htonT<unsigned int>(unsigned int): # @std::enable_if<(sizeof (unsigned int))==(4), unsigned int>::type htonT<unsigned int>(unsigned int)
    bswapl  %edi
    movl    %edi, %eax
    retq

std::enable_if<(sizeof (unsigned long))==(8), unsigned long>::type htonT<unsigned long>(unsigned long): # @std::enable_if<(sizeof (unsigned long))==(8), unsigned long>::type htonT<unsigned long>(unsigned long)
    bswapq  %rdi
    movq    %rdi, %rax
    retq
_

注: 元の回答 はC++ 11 -constexprに準拠していませんでした。

この回答は Public Domain CC0 1.0 Universal にあります

11
olibre

リトルエンディアンマシンでの64ビットスワップ用の1行マクロ。

#define bswap64(y) (((uint64_t)ntohl(y)) << 32 | ntohl(y>>32))
6
Sandeep
uint32_t SwapShort(uint16_t a)
{
  a = ((a & 0x00FF) << 8) | ((a & 0xFF00) >> 8);
  return a;
}

uint32_t SwapWord(uint32_t a)
{
  a = ((a & 0x000000FF) << 24) |
      ((a & 0x0000FF00) <<  8) |
      ((a & 0x00FF0000) >>  8) |
      ((a & 0xFF000000) >> 24);
  return a;
}

uint64_t SwapDWord(uint64_t a)
{
  a = ((a & 0x00000000000000FFULL) << 56) | 
      ((a & 0x000000000000FF00ULL) << 40) | 
      ((a & 0x0000000000FF0000ULL) << 24) | 
      ((a & 0x00000000FF000000ULL) <<  8) | 
      ((a & 0x000000FF00000000ULL) >>  8) | 
      ((a & 0x0000FF0000000000ULL) >> 24) | 
      ((a & 0x00FF000000000000ULL) >> 40) | 
      ((a & 0xFF00000000000000ULL) >> 56);
  return a;
}
5
kuzne4ik

入力サイズに依存しない汎用バージョンはどうでしょうか(上記の実装の一部はunsigned long longは64ビットであり、必ずしも常に正しいとは限りません)。

    // converts an arbitrary large integer (preferrably >=64 bits) from big endian to Host machine endian
    template<typename T> static inline T bigen2Host(const T& x)
    {
        static const int one = 1;
        static const char sig = *(char*)&one; 

        if (sig == 0) return x; // for big endian machine just return the input

        T ret;
        int size = sizeof(T);
        char* src = (char*)&x + sizeof(T) - 1;
        char* dst = (char*)&ret;

        while (size-- > 0) *dst++ = *src--;

        return ret;
    }
5
Andrei Bozantan

どうですか:

#define ntohll(x) ( ( (uint64_t)(ntohl( (uint32_t)((x << 32) >> 32) )) << 32) | 
    ntohl( ((uint32_t)(x >> 32)) ) )                                        
#define htonll(x) ntohll(x)
3
Matt

私は結束の答えがとても好きです。通常、私はリトルエンディアンとビッグエンディアンの間で変換するためにビットシフトしますが、ユニオンソリューションは割り当てが少なく、高速であると思います。

//note UINT64_C_LITERAL is a macro that appends the correct prefix
//for the literal on that platform
inline void endianFlip(unsigned long long& Value)
{
   Value=
   ((Value &   UINT64_C_LITERAL(0x00000000000000FF)) << 56) |
   ((Value &   UINT64_C_LITERAL(0x000000000000FF00)) << 40) |
   ((Value &   UINT64_C_LITERAL(0x0000000000FF0000)) << 24) |
   ((Value &   UINT64_C_LITERAL(0x00000000FF000000)) << 8)  |
   ((Value &   UINT64_C_LITERAL(0x000000FF00000000)) >> 8)  | 
   ((Value &   UINT64_C_LITERAL(0x0000FF0000000000)) >> 24) |
   ((Value &   UINT64_C_LITERAL(0x00FF000000000000)) >> 40) |
   ((Value &   UINT64_C_LITERAL(0xFF00000000000000)) >> 56);
}

次に、マクロマジックなしでフリップを行う必要があるかどうかを検出するために、Paxと同様のことができます。ショートが0x0001に割り当てられると、反対側のエンディアンシステムでは0x0100になります。

そう:

unsigned long long numberToSystemEndian
(
    unsigned long long In, 
    unsigned short SourceEndian
)
{
   if (SourceEndian != 1)
   {
      //from an opposite endian system
      endianFlip(In);
   }
   return In;
}

したがって、これを使用するには、SourceEndianが入力番号のエンディアンを伝えるインジケーターである必要があります。これはファイルに保存するか(これがシリアル化の問題である場合)、ネットワーク経由で通信することができます(ネットワークのシリアル化の問題である場合)。

2
Snazzer

簡単な方法は、2つの部分でntohlを別々に使用することです。

unsigned long long htonll(unsigned long long v) {
    union { unsigned long lv[2]; unsigned long long llv; } u;
    u.lv[0] = htonl(v >> 32);
    u.lv[1] = htonl(v & 0xFFFFFFFFULL);
    return u.llv;
}

unsigned long long ntohll(unsigned long long v) {
    union { unsigned long lv[2]; unsigned long long llv; } u;
    u.llv = v;
    return ((unsigned long long)ntohl(u.lv[0]) << 32) | (unsigned long long)ntohl(u.lv[1]);
}
2
bdonlan

htonlは以下の手順で実行できます

  • ビッグエンディアンシステムが値を直接返す場合。変換を行う必要はありません。そのリテンディアンシステムの場合、以下の変換を行う必要があります。
  • LSB 32ビットを取得し、「htonl」を適用して32回シフトします。
  • MSB 32ビットを取得し(uint64_t値を32回右にシフト)、「htonl」を適用します
  • ここで、2番目と3番目のステップで受信した値にビット単位のORを適用します。

同様にntohllについても

#define HTONLL(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32)))
#define NTOHLL(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32)))

上記の2つの定義を関数として使用することもできます。

1
rashok
template <typename T>
static T ntoh_any(T t)
{
    static const unsigned char int_bytes[sizeof(int)] = {0xFF};
    static const int msb_0xFF = 0xFF << (sizeof(int) - 1) * CHAR_BIT;
    static bool Host_is_big_endian = (*(reinterpret_cast<const int *>(int_bytes)) & msb_0xFF ) != 0;
    if (Host_is_big_endian) { return t; }

    unsigned char * ptr = reinterpret_cast<unsigned char *>(&t);
    std::reverse(ptr, ptr + sizeof(t) );
    return t;
}

2バイト、4バイト、8バイト、および16バイトで動作します(128ビットの整数がある場合)。 OS /プラットフォームに依存しない必要があります。

0
zhaorufei

任意の値サイズのユニバーサル関数。

template <typename T>
T swap_endian (T value)
{
    union {
        T src;
        unsigned char dst[sizeof(T)];
    } source, dest;

    source.src = value;
    for (size_t k = 0; k < sizeof(T); ++k)
        dest.dst[k] = source.dst[sizeof(T) - k - 1];

    return dest.src;
}
0
Виталий

これは、64ビットOSを使用してLinuxでコーディングしていると仮定しています。ほとんどのシステムにはhtole(x)ntobe(x)などがあります。これらは通常、さまざまなbswapのマクロです。

#include <endian.h>
#include <byteswap.h>

unsigned long long htonll(unsigned long long val)
{
    if (__BYTE_ORDER == __BIG_ENDIAN) return (val);
    else return __bswap_64(val);
}

unsigned long long ntohll(unsigned long long val)
{
    if (__BYTE_ORDER == __BIG_ENDIAN) return (val);
    else return __bswap_64(val);
}

サイドノート;これらは、バイト順序を交換するために呼び出す関数です。ビッグエンディアンネットワークなどでリトルエンディアンを使用しているが、ビッグエンディングエンコーディングを使用している場合、バイトオーダーが不必要に逆になり、「if __BYTE_ORDER == __LITTLE_ENDIAN "チェックは、コードの移植性を高めるために必要な場合があり、必要に応じて使用します。

更新:エンディアンチェックの例を表示するように編集

0
jwbensley