htonl()
のmanページは、32ビット値までしか使用できないことを示唆しているようです。 (実際には、ntohl()
は、私のプラットフォームでは32ビットであるunsigned longに対して定義されています。unsignedlongが8バイトであれば、64ビットintで機能すると思います)。
私の問題は、64ビット整数(私の場合、これは符号なしlong long)をビッグエンディアンからリトルエンディアンに変換する必要があることです。今、私はその特定の変換を行う必要があります。ただし、ターゲットプラットフォームがビッグエンディアンの場合、関数(ntohl()
など)が私の64ビット値を変換しないとさらに良いでしょう。 (これを行うために独自のプリプロセッサマジックを追加するのは避けたいです)。
何が使えますか?存在する場合は標準的なものが欲しいのですが、実装の提案は受け付けています。過去にこの種の変換がユニオンを使用して行われたのを見てきました。符号なしのlong longとchar [8]を持つユニオンを持つことができると思います。次に、それに応じてバイトを交換します。 (明らかに、ビッグエンディアンのプラットフォームでは動作しません)。
ドキュメンテーション: 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ライブラリ」のようなアプローチです。
これを読むことをお勧めします: 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ビットも安全です!
エンディアンを検出するには、次のユニオンを使用します。
_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
_
一部のBSDシステムにはbetoh64
必要なことを行います。
_#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
_
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;
}
_
constexpr
テンプレート関数constexpr
関数でローカル変数を使用できません。constexpr
関数には、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 で出力を実行します)。
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';
_
_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 にあります
リトルエンディアンマシンでの64ビットスワップ用の1行マクロ。
#define bswap64(y) (((uint64_t)ntohl(y)) << 32 | ntohl(y>>32))
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;
}
入力サイズに依存しない汎用バージョンはどうでしょうか(上記の実装の一部は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;
}
どうですか:
#define ntohll(x) ( ( (uint64_t)(ntohl( (uint32_t)((x << 32) >> 32) )) << 32) |
ntohl( ((uint32_t)(x >> 32)) ) )
#define htonll(x) ntohll(x)
私は結束の答えがとても好きです。通常、私はリトルエンディアンとビッグエンディアンの間で変換するためにビットシフトしますが、ユニオンソリューションは割り当てが少なく、高速であると思います。
//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つの部分で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]);
}
htonl
は以下の手順で実行できます
同様に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つの定義を関数として使用することもできます。
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 /プラットフォームに依存しない必要があります。
任意の値サイズのユニバーサル関数。
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;
}
これは、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
"チェックは、コードの移植性を高めるために必要な場合があり、必要に応じて使用します。
更新:エンディアンチェックの例を表示するように編集