هل هناك أي وظيفة "قياسية" تشبه HTONL لـ 64 بت أعداد صحيحة في C ++؟
-
26-09-2019 - |
سؤال
أنا أعمل على تنفيذ بروتوكول Memcache الذي يستخدم في بعض النقاط 64 بتات عدد صحيح. يجب تخزين هذه القيم في "ترتيب بايت الشبكة".
أتمنى لو كان هناك بعض uint64_t htonll(uint64_t value)
وظيفة لإجراء التغيير ، ولكن لسوء الحظ ، إذا كان موجودًا ، لم أتمكن من العثور عليه.
لذلك لدي سؤال واحد أو 2:
- هل هنالك أي محمول (Windows ، Linux ، AIX) وظيفة قياسية للقيام بذلك؟
- إذا لم تكن هناك وظيفة من هذا القبيل ، كيف يمكنك تنفيذها؟
لقد وضعت في اعتبارك تطبيقًا أساسيًا ، لكنني لا أعرف كيفية التحقق من endianness في وقت الترجمة لجعل الكود محمول. لذا فإن مساعدتك أكثر من موضع ترحيب هنا ؛)
شكرًا لك.
إليكم الحل النهائي الذي كتبته ، بفضل حل براين.
uint64_t htonll(uint64_t value)
{
// The answer is 42
static const int num = 42;
// Check the endianness
if (*reinterpret_cast<const char*>(&num) == num)
{
const uint32_t high_part = htonl(static_cast<uint32_t>(value >> 32));
const uint32_t low_part = htonl(static_cast<uint32_t>(value & 0xFFFFFFFFLL));
return (static_cast<uint64_t>(low_part) << 32) | high_part;
} else
{
return value;
}
}
المحلول
ربما تبحث عنه bswap_64
أعتقد أنه مدعوم إلى حد كبير في كل مكان ولكني لن أسميه قياسيًا.
يمكنك بسهولة التحقق من endianness عن طريق إنشاء int بقيمة 1 ، وإلقاء عنوان int الخاص بك كملف char*
والتحقق من قيمة البايت الأول.
علي سبيل المثال:
int num = 42;
if(*(char *)&num == 42)
{
//Little Endian
}
else
{
//Big Endian
}
مع العلم أن يمكنك أيضًا عمل وظيفة بسيطة تقوم بالمبادلة.
يمكنك أيضًا استخدام Boost دائمًا الذي يحتوي على وحدات الماكرو الإنديان التي هي منصة متقاطعة محمولة.
نصائح أخرى
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
الاختبار (1 == htonl (1)) يحدد ببساطة (في وقت التشغيل للأسف) إذا كانت بايت بنية الأجهزة تبديل بايت. لا توجد أي طرق محمولة لتحديد وقت الترجمة ماهية البنية ، لذلك نلجأ إلى استخدام "Htonl" ، وهو محمول كما هو الحال في هذا الموقف. إذا كانت هناك حاجة إلى تبادل البايت ، فنحن نقوم بتبديل 32 بت في وقت واحد باستخدام HTONL (نتذكر تبديل كلمتين 32 بت أيضًا).
فيما يلي طريقة أخرى لإجراء المبادلة المحمولة عبر معظم المجمعين وأنظمة التشغيل ، بما في ذلك AIX و BSDs و Linux و Solaris.
#if __BIG_ENDIAN__
# define htonll(x) (x)
# define ntohll(x) (x)
#else
# define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
# define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif
الجزء المهم هو الاستخدام __BIG_ENDIAN__
أو __LITTLE_ENDIAN__
; ؛ و لا __BYTE_ORDER__
, __ORDER_BIG_ENDIAN__
أو __ORDER_LITTLE_ENDIAN__
. يفتقر بعض المجمعين وأنظمة التشغيل __BYTE_ORDER__
والأصدقاء.
يمكنك المحاولة uint64_t htobe64(uint64_t host_64bits)
&
uint64_t be64toh(uint64_t big_endian_64bits)
للعكس بالعكس.
يبدو أن هذا يعمل في ج. هل قمت بأي تصرف خاطئ؟
uint64_t htonll(uint64_t value) {
int num = 42;
if (*(char *)&num == 42) {
uint32_t high_part = htonl((uint32_t)(value >> 32));
uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
return (((uint64_t)low_part) << 32) | high_part;
} else {
return value;
}
}
لتقليل النفقات العامة لـ "if num == ..." ، استخدم المعالج المسبق:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif
تحرير: الجمع بين الاثنين (كود براين المستخدم):
uint64_t htonll(uint64_t value)
{
int num = 42;
if(*(char *)&num == 42)
return (htonl(value & 0xFFFFFFFF) << 32LL) | htonl(value >> 32);
else
return value;
}
تحذير: رمز لم يخبر! الرجاء الاختبار قبل استخدام.