64 Bit Ntohl() in C++

Is there any standard htonl-like function for 64 bits integers in C++?

You are probably looking for bswap_64 I think it is supported pretty much everywhere but I wouldn't call it standard.

You can easily check the endianness by creating an int with a value of 1, casting your int's address as a char* and checking the value of the first byte.

For example:

int num = 42;
if(*(char *)&num == 42)
{
//Little Endian
}
else
{
//Big Endian
}

Knowing this you could also make a simple function that does the swapping.


You could also always use boost which contains endian macros which are portable cross platform.

Portable way of sending 64-bit variable through POSIX socket

You can just apply htonl() twice, of course:

const uint64_t x = ...
const uint32_t upper_be = htonl(x >> 32);
const uint32_t lower_be = htonl((uint32_t) x);

This will give you two 32-bit variables containing big-endian versions of the upper and lower 32-bit halves of the 64-bit variable x.

If you are strict POSIX, you can't use uint64_t since it's not guaranteed to exist. Then you can do something like:

typedef struct {
uint32_t upper;
uint32_t lower;
} my_uint64;

And just htonl() those directly, of course.

Efficiently bitshifting bytes in 32/64 bit quantities?

Use htonl/ntohl to flip between network (big-endian) byte order and native byte order:

uint32_t *p = (uint32_t*)buffer;
*p = htonl(ntohl(*p) << 4);

In effect, this loads the buffer contents as an integer in big-endian order, performs the shift, then writes it back in big-endian order.

This compiles into a couple of bswap instructions on x86, so it should be reasonably efficient (gcc -O3).


Here's some test code (buffer is global to avoid constant-folding, and the return prevents dead-code elimination):

#include <stdint.h>    // uint32_t
#include <arpa/inet.h> // ntohl, htonl

unsigned char buffer[] = { 0xab, 0xcd, 0xef, 0x46 };

int main() {
uint32_t *p = (uint32_t*)buffer; // unsigned int is 32 bit on my platform
*p = htonl(ntohl(*p) << 4);
return *p;
}

This compiles into the following fairly simple machine code (x86-64; LLVM 7.0.2; cc -O2):

0000000000000000    pushq   %rbp           ; frame setup
0000000000000001 movq %rsp, %rbp ; frame setup
0000000000000004 movl (%rip), %eax ; load buffer
000000000000000a bswapl %eax ; endian flip
000000000000000c shll $0x4, %eax ; shift
000000000000000f bswapl %eax ; endian flip
0000000000000011 movl %eax, (%rip) ; save buffer
0000000000000017 popq %rbp ; finish
0000000000000018 retq

Endian representation of 64-bit values

The first one is wrong. On ia32 at least the layout is EF CD AB 89 67 45 23 01.

The others are correct.

C++ htonll and back

The code for your htonll

#define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl((x) >> 32))

flips the bytes end to end. If you apply it twice, it restores the value to its original state. So the same function can be used for ntohll.

Reverse bytes for 64-bit value

You cannot use char x for a pointer!!!! A char is only a single byte long.

You need at the very least

unsigned long int swapPtr(unsigned long int x) {

Or better, use the type of the pointer

void* swapPtr(void* x) {

Quite likely your compiler will complain when you start bit shifting pointers; in that case you're better off explicitly casting your argument to an unsigned 64 bit integer:

#include <stdint.h>
uint64_t x;

Note also that you have to call with the address of a variable, so you call with

result = swapLong(&loc);

not *loc (which looks at the place where loc is pointing - the value, not the address).

Complete program:

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

uint64_t swapLong(void *X) {
uint64_t x = (uint64_t) X;
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
return x;
}

int main(void) {
char a;
printf("the address of a is 0x%016llx\n", (uint64_t)(&a));
printf("swapping all the bytes gives 0x%016llx\n",(uint64_t)swapLong(&a));
}

Output:

the address of a is 0x00007fff6b133b1b
swapping all the bytes gives 0x1b3b136bff7f0000

EDIT you could use something like

#include <inttypes.h>

printf("the address of a is 0x%016" PRIx64 "\n", (uint64_t)(&a));

where the macro PRIx64 expands into "the format string you need to print a 64 bit number in hex". It is a little cleaner than the above.

Convert 64 bit unsigned int to char buffer back and forth in C

Your serializer is essentially

unsigned char *serialize_u64(unsigned char *buffer, uint64_t value)
{
buffer[7] = value & 0xFF;
value >>= 8;
buffer[6] = value & 0xFF;
value >>= 8;
buffer[5] = value & 0xFF;
value >>= 8;
buffer[4] = value & 0xFF;
value >>= 8;
buffer[3] = value & 0xFF;
value >>= 8;
buffer[2] = value & 0xFF;
value >>= 8;
buffer[1] = value & 0xFF;
value >>= 8;
buffer[0] = value & 0xFF;
return buffer + 8;
}

and it serializes value from native byte order to network byte order; no macro is needed.

So, it looks like OP's serialize_uint64() should work just fine. It's just that no byte order macro should be used at all.

OP's deserialize_uint64() should cast buffer[i] to (uint64_t) before shifting, to ensure the shifted result is 64-bit. Personally, I prefer to write the deserializer as

unsigned char *serialize_u64(unsigned char *buffer, uint64_t *valueptr)
{
uint64_t value = buffer[0];
value <<= 8;
value |= buffer[1];
value <<= 8;
value |= buffer[2];
value <<= 8;
value |= buffer[3];
value <<= 8;
value |= buffer[4];
value <<= 8;
value |= buffer[5];
value <<= 8;
value |= buffer[6];
value <<= 8;
value |= buffer[7];
*valueptr = value;
return buffer + 8;
}

which does the equivalent operation as OP's, if OP used res |= ((uint64_t)buffer[i]) << (56 - 8 * i); instead.

Again, both serializer and deserializer already convert the data to/from network byte order from/to native byte order; no byte order macros should be used at all.

Using htonl/ntohl on a 32 bit integer that will be sent over a socket

u_long is a typedef to unsigned long, long in turn is (or should be) guaranteed to be at least 32 bits - that is at least 4 bytes. On some systems it may be larger - but there's really no way to know in advance.

So, when you do network communication and want to send integers that's larger than one byte you have to take care to restrict the size yourself. Don't just send sizeof bytes, restrict it to four bytes.

Also when dealing with integers you have the little matter of which byte order is used to send/receive the data.
If you have the same OS both sides, this won't be an issue - but if you switch between Windows and Linux for example, it could be.



Related Topics



Leave a reply



Submit