c++ - सी++ में 64 बिट ntohl()?




linux 64bit (11)

शीघ्र जवाब

uint64_t value = 0x1122334455667788;
#if __BYTE_ORDER == __LITTLE_ENDIAN
value = __bswap_constant_64(value);  // Compiler builtin
#endif

यह उत्तर शेष सी ++ में 16 बिट्स, 32 बिट्स, 64 बिट्स के लिए जेनेरिक hton() बारे में है।

सी ++ 14 constexpr टेम्पलेट समारोह

#include <endian.h>    // __BYTE_ORDER
#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;
}

सी ++ 11 constexpr टेम्पलेट समारोह

  • सी ++ 11 स्थानीय चर को constexpr फ़ंक्शन में अनुमति नहीं देता है।
    इसलिए चाल डिफ़ॉल्ट मान के साथ एक तर्क का उपयोग करना है।
  • इसके अलावा सी ++ 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;
}

-Wall -Wextra -pedantic और जीसीसी -Wall -Wextra -pedantic दोनों पर कोई संकलन चेतावनी नहीं -Wall -Wextra -pedantic ( coliru पर संकलन और रन आउटपुट coliru )।

सी ++ 11 constexpr टेम्पलेट SFINAE कार्यों

हालांकि उपर्युक्त संस्करण constexpr चर बनाने के लिए अनुमति नहीं देता है:

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

अंत में हमें 16/32/64 बिट्स के आधार पर कार्यों को अलग (विशेषज्ञ) करने की आवश्यकता है।
लेकिन हम अभी भी सामान्य कार्यों को रख सकते हैं।
=> coliru पर पूरा स्निपेट देखें।

नीचे सी ++ 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);
}

या अंतर्निर्मित संकलक मैक्रोज़ और सी ++ 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_constant_16(value);
}

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

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

पहले संस्करण का टेस्ट कोड

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

कोड पीढ़ी

ऑनलाइन सी ++ कंपाइलर 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

नोट: मेरा मूल उत्तर सी ++ 11- constexpr अनुपालन नहीं था।

यह उत्तर सार्वजनिक डोमेन CC0 1.0 सार्वभौमिक में है

htonl() लिए मैन पेज सुझाव देते हैं कि आप केवल 32 बिट मानों के लिए इसका उपयोग कर सकते हैं। (हकीकत में, ntohl() को बिना हस्ताक्षर किए गए लंबे समय तक परिभाषित किया गया है, जो कि मेरे प्लेटफॉर्म पर 32 बिट्स है। मुझे लगता है कि अगर हस्ताक्षर किए गए लंबे 8 बाइट थे, तो यह 64 बिट इन्ट्स के लिए काम करेगा)।

मेरी समस्या यह है कि मुझे 64 बिट पूर्णांक को परिवर्तित करने की आवश्यकता है (मेरे मामले में, यह एक लंबे समय तक एक हस्ताक्षरित लंबा लंबा है) बड़े एंडियन से थोड़ा एंडियन तक। अभी, मुझे उस विशिष्ट रूपांतरण की आवश्यकता है। लेकिन यह भी अच्छा होगा अगर फ़ंक्शन (जैसे ntohl() ) मेरे 64 बिट मान को परिवर्तित नहीं करेगा यदि लक्ष्य प्लेटफ़ॉर्म बड़ा अंतराल था। (मैं ऐसा करने के लिए अपने स्वयं के प्रीप्रोसेसर जादू जोड़ने से बचाना चाहूंगा)।

मैं क्या उपयोग कर सकता हूँ मुझे कुछ ऐसा मानक चाहिए जो मानक है यदि यह मौजूद है, लेकिन मैं कार्यान्वयन सुझावों के लिए खुला हूं। मैंने यूनियनों का उपयोग करके अतीत में इस तरह के रूपांतरण को देखा है। मुझे लगता है कि मैं एक हस्ताक्षरित लंबे लंबे और एक char [8] के साथ एक संघ हो सकता है। फिर तदनुसार बाइट्स को स्वैप करें। (स्पष्ट रूप से प्लेटफार्मों को तोड़ देगा जो बड़े एंडियन थे)।


अपने एंडियन-नेस का पता लगाने के लिए, निम्नलिखित संघ का उपयोग करें:

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

फिर आप यह पता लगाने के लिए xc[] की सामग्री की जांच कर सकते हैं कि प्रत्येक बाइट कहाँ गया था।

रूपांतरण करने के लिए, मैं उस पहचान कोड का उपयोग एक बार देखने के लिए करता हूं कि मंच क्या एंडियन-नेस का उपयोग कर रहा है, फिर स्वैप करने के लिए अपना स्वयं का फ़ंक्शन लिखें।

आप इसे गतिशील बना सकते हैं ताकि कोड किसी भी प्लेटफॉर्म पर चलाए जा सके (एक बार पता लगाएं कि सही रूपांतरण चुनने के लिए अपने रूपांतरण कोड के अंदर एक स्विच का उपयोग करें), लेकिन, यदि आप केवल एक मंच का उपयोग करने जा रहे हैं, तो मैं बस करूँगा एक अलग कार्यक्रम में एक बार पता लगाने के बाद, एक साधारण रूपांतरण दिनचर्या को कोड करें, यह सुनिश्चित कर लें कि आप दस्तावेज़ को केवल उस प्लेटफॉर्म पर चलाएं (या परीक्षण किया गया है)।

यहां कुछ नमूना कोड दिए गए हैं जिन्हें मैंने चित्रित करने के लिए चाबुक किया था। हालांकि यह पूरी तरह से परीक्षण नहीं किया गया है, लेकिन शुरू करने के लिए पर्याप्त होना चाहिए।

#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

किसी भी मूल्य आकार के लिए सार्वभौमिक समारोह।

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

कुछ बीएसडी सिस्टम में betoh64 जो आपको चाहिए।


छोटी एंडियन मशीनों पर 64 बिट स्वैप के लिए एक लाइन मैक्रो।

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

दो भागों पर 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]);
}

प्रलेखन: लिनक्स पर man htobe64 (glibc> = 2.9) या फ्रीबीएसडी।

दुर्भाग्य से ओपनबीएसडी, फ्रीबीएसडी और ग्लिबैक (लिनक्स) 200 9 में एक प्रयास के दौरान एक (गैर-कर्नेल-एपीआई) 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

(लिनक्स और ओपनबीएसडी पर परीक्षण) अंतर को छिपाना चाहिए। यह आपको उन 4 प्लेटफॉर्म पर लिनक्स / फ्रीबीएसडी-शैली मैक्रोज़ देता है।

उदाहरण का प्रयोग करें:

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

यह फिलहाल उपलब्ध सबसे मानक "मानक सी लाइब्रेरी" है।


मुझे यूनियन उत्तर पसंद है, बहुत साफ है। आम तौर पर मैं थोड़ा और बड़ा एंडियन के बीच परिवर्तित करने के लिए बस थोड़ा सा बदलाव करता हूं, हालांकि मुझे लगता है कि संघ समाधान में कम असाइनमेंट हैं और तेज़ी से हो सकते हैं:

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

फिर यह पता लगाने के लिए कि क्या आपको मैक्रो जादू के बिना अपनी फ्लिप करने की भी आवश्यकता है, आप एक समान चीज़ को पैक्स के रूप में कर सकते हैं, जहां 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 की आवश्यकता होगी। यह फ़ाइल में संग्रहीत किया जा सकता है (यदि यह एक क्रमबद्धता समस्या है), या नेटवर्क पर संचारित किया गया है (यदि यह नेटवर्क क्रमबद्धता मुद्दा है)।


यह माना जा रहा है कि आप 64 बिट ओएस का उपयोग कर लिनक्स पर कोडिंग कर रहे हैं; अधिकांश प्रणालियों में 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 " चेक आपके कोड को और पोर्टेबल बनाने की आवश्यकता हो सकती है , आपकी जरूरतों को पूरा करना।

अद्यतन: एंडियन चेक का उदाहरण दिखाने के लिए संपादित किया गया


htonl नीचे चरणों से किया जा सकता है

  • यदि इसकी बड़ी एंडियन प्रणाली सीधे मूल्य वापस कर देती है। कोई रूपांतरण करने की जरूरत नहीं है। यदि इसकी लिट एंडियन प्रणाली है, तो नीचे रूपांतरण करने की आवश्यकता है।
  • एलएसबी 32 बिट लें और 'htonl' लागू करें और 32 बार शिफ्ट करें।
  • एमएसबी 32 बिट लें (uint64_t मान 32 गुना दाएं स्थानांतरित करके) और '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 परिभाषाओं से ऊपर की देखभाल कर सकते हैं।


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




endianness