IT박스

C ++의 64 비트 ntohl ()?

itboxs 2020. 12. 11. 07:59
반응형

C ++의 64 비트 ntohl ()?


의 man 페이지는 htonl()최대 32 비트 값에만 사용할 수 있다고 제안하는 것 같습니다. (실제로 ntohl()는 내 플랫폼에서 32 비트 인 unsigned long으로 정의됩니다. unsigned long이 8 바이트이면 64 비트 int에서 작동한다고 가정합니다.)

내 문제는 64 비트 정수 (제 경우에는 부호없는 long long)를 빅 엔디안에서 리틀 엔디안으로 변환해야한다는 것입니다. 지금 당장 특정 변환을 수행해야합니다. 그러나 ntohl()대상 플랫폼이 빅 엔디안이라면 함수 (예 :)가 내 64 비트 값을 변환하지 않으면 더 좋을 것입니다. (차라리이 작업을 수행하기 위해 나만의 전 처리기 마법을 추가하는 것을 피하고 싶습니다).

무엇을 사용할 수 있습니까? 존재한다면 표준적인 것을 원하지만 구현 제안에 개방적입니다. 나는 과거에 공용체를 사용하여 이러한 유형의 변환을 수행 한 것을 보았습니다. unsigned long long과 char [8]이있는 공용체를 가질 수 있다고 가정합니다. 그런 다음 그에 따라 바이트를 바꿉니다. (분명히 빅 엔디안이었던 플랫폼에서 깨질 것입니다).


문서 : man htobe64Linux (glibc> = 2.9) 또는 FreeBSD.

불행히도 OpenBSD, FreeBSD 및 glibc (리눅스)는 2009 년 시도 동안이를위한 하나의 (비 커널 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[]각 바이트가 어디로 갔는지 감지 할 수 있습니다 .

변환을 수행하기 위해 해당 감지 코드를 한 번 사용하여 플랫폼이 사용하는 엔디안을 확인한 다음 스왑을 수행하는 내 자신의 함수를 작성합니다.

코드가 모든 플랫폼에서 실행되도록 동적으로 만들 수 있지만 (한 번 감지 한 다음 전환 코드 내부의 스위치를 사용하여 올바른 변환을 선택합니다)하지만 하나의 플랫폼 만 사용하려는 경우에는 별도의 프로그램에서 한 번 탐지 한 다음 간단한 변환 루틴을 코딩하여 해당 플랫폼에서만 실행 (또는 테스트)되었음을 문서화합니다.

다음은이를 설명하기 위해 작성한 몇 가지 샘플 코드입니다. 철저하지는 않지만 테스트를 거쳤지만 시작하기에 충분합니다.

#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

더 이식 가능한 소스 코드도 참조하십시오 : 크로스 플랫폼_byteswap_uint64

C ++ 14 constexpr템플릿 함수

hton()16 비트, 32 비트, 64 비트 등에 대한 일반 ...

#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함수는 하나의 단일 표현식을 포함해야합니다.
    따라서 본문은 쉼표로 구분 된 명령문 이있는 하나의 리턴으로 구성됩니다 .
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;
}

clang-3.5 및 GCC-4.9 모두에서 컴파일 경고가 발생하지 않습니다 -Wall -Wextra -pedantic
( coliru 에서 컴파일 및 실행 출력 참조 ).

C ++ 11 constexpr템플릿 SFINAE 함수

그러나 위 버전에서는 다음과 같이 constexpr변수 를 생성 할 수 없습니다 .

constexpr int32_t hton_six = htonT( int32_t(6) );

마지막으로 16/32/64 비트에 따라 기능을 분리 (특화)해야합니다.
그러나 우리는 여전히 일반적인 기능을 유지할 수 있습니다.
( coliru 에 대한 전체 스 니펫 참조 )

아래 C ++ 11 스 니펫은 특성 std::enable_if사용하여 SFINAE ( Substitution Failure Is Not An Error) 를 악용 합니다.

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);
}

또는 내장 컴파일러 매크로와 C ++ 14 구문 std::enable_if_t<xxx>기반으로하는 더 짧은 버전을 다음에 대한 단축키로 사용할 수 있습니다 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 비트 스왑을위한 한 줄 매크로.

#define bswap64(y) (((uint64_t)ntohl(y)) << 32 | ntohl(y>>32))

입력 크기에 의존하지 않는 제네릭 버전은 어떻습니까 (위의 구현 중 일부 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;
    }

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;
}

어때 :

#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와 비슷한 작업을 수행 할 수 있습니다. 여기서 short가 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이 입력 번호의 엔디안을 전달하는 지표가 될 필요가 있습니다. 이것은 파일에 저장되거나 (직렬화 문제인 경우) 네트워크를 통해 통신 될 수 있습니다 (네트워크 직렬화 문제인 경우).


쉬운 방법은 두 부분에서 따로 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 아래 단계로 수행 할 수 있습니다.

  • 빅 엔디안 시스템이 값을 직접 반환하는 경우. 변환 할 필요가 없습니다. litte endian 시스템의 경우 아래 변환을 수행해야합니다.
  • LSB 32 비트를 가져와 'htonl'을 적용하고 32 번 시프트합니다.
  • MSB 32 비트 (uint64_t 값을 오른쪽으로 32 배 이동)를 취하고 'htonl'을 적용합니다.
  • 이제 두 번째 및 세 번째 단계에서받은 값에 대해 비트 단위 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 이상의 정의를 함수로도 설명 할 수 있습니다.


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 / 플랫폼에 독립적이어야합니다.


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);
}

Side note; these are just functions to call to swap the byte ordering. If you are using little endian for example with a big endian network, but if you are using big ending encoding then this will unnecessarily reverse the byte ordering so a little "if __BYTE_ORDER == __LITTLE_ENDIAN" check might be require to make your code more portable, depening on your needs.

Update: Edited to show example of endian check


universal function for any value size.

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;
}

It isn't in general necessary to know the endianness of a machine to convert a host integer into network order. Unfortunately that only holds if you write out your net-order value in bytes, rather than as another integer:

static inline void short_to_network_order(uchar *output, uint16_t in)
{
    output[0] = in>>8&0xff;
    output[1] = in&0xff;
}

(extend as required for larger numbers).

This will (a) work on any architecture, because at no point do I use special knowledge about the way an integer is laid out in memory and (b) should mostly optimise away in big-endian architectures because modern compilers aren't stupid.

The disadvantage is, of course, that this is not the same, standard interface as htonl() and friends (which I don't see as a disadvantage, because the design of htonl() was a poor choice imo).

참고URL : https://stackoverflow.com/questions/809902/64-bit-ntohl-in-c

반응형