Reverse bytes for 64-bit value
Asked Answered
B

3

7

I'm trying to reverse the bytes for a 64 bit address pointer for an assignment and have this code:

char swapPtr(char x){
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
x = (x & 0x00FF00FF00FF00FF) << 8  | (x & 0xFF00FF00FF00FF00) >> 8;
return x;
}

But, it just messes everything up. However, a similar function works perfectly for a 64bit long. Is there something different that needs to be done for pointers?

Could the way I'm making the function call be an issue?

For a pointer:

*(char*)loc = swapPtr(*(char*)loc);

For a long:

*loc = swapLong(*loc);
Bryant answered 2/2, 2014 at 6:13 Comment(3)
char is not the right datatype. It's (usually) one 8bit byte. If you're on a platform with 64bit chars, please mention that.Bohemian
yes, it is on the x86_64 architectureBryant
Then a char is 8 bits. It can't possibly hold a 64bit value.Bohemian
C
9

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.

Cyclamate answered 2/2, 2014 at 6:19 Comment(4)
Just don't use fixed width types for such a task. The appropriate type is uintptr_t. In addition you could "#if UINTPRT_SIZE > UINT32_SIZE" the part that supposes that the width is 64 bit.Peignoir
@jensgustedt thanks for the clarification. I didn't realize there was a uintptr_t - should have thought to look. Learning something every day!Cyclamate
Minor: Consider printf("0x016" PRIx64, (uint64_t)(&a)) or printf("0x%016llx\n", (unsigned long long)(&a)) instead of as posted to get the format and integer to match?Grazier
@chux - that would indeed be better. There are several ways this could be improved... Might do so when I am near a computer (rather than phone)Cyclamate
P
4

You may also use _bswap64 intrinsic (which has latency of 2 and a throughput of 0.5 on Skylake Architecture). It is a wrapper for the assembly instruction bswap r64 so probably the most efficient :

Reverse the byte order of 64-bit integer a, and store the result in dst. This intrinsic is provided for conversion between little and big endian values.

#include <immintrin.h>

uint64_t swapLongIntrinsic(void *X) {
    return __bswap_64((uint64_t) X);
}

NB: Don't forget the header

Phocomelia answered 10/4, 2020 at 3:28 Comment(0)
S
3

Here is an alternative way for converting a 64-bit value from LE to BE or vice-versa.

You can basically apply this method any type, by defining var_type:

typedef long long var_type;

Reverse by pointer:

void swapPtr(var_type* x)
{
    char* px = (char*)x;
    for (int i=0; i<sizeof(var_type)/2; i++)
    {
        char temp = px[i];
        px[i] = px[sizeof(var_type)-1-i];
        px[sizeof(var_type)-1-i] = temp;
    }
}

Reverse by value:

var_type swapVal(var_type x)
{
    var_type y;
    char* px = (char*)&x;
    char* py = (char*)&y;
    for (int i=0; i<sizeof(var_type); i++)
        py[i] = px[sizeof(var_type)-1-i];
    return y;
}
Shanell answered 2/2, 2014 at 6:36 Comment(6)
+1 for nice generic solution. (Maybe uint8_t rather than char if one wanted to insure an 8-bit byte swap?)Grazier
Thank you chux. I'm pretty sure that char is defined as a single byte in the standard. Doesn't that make the "mapping" between LE and BE accordingly? In other words, the order of bytes in a multi-byte word on an LE processor is opposite to the one on a BE processor, regardless of the size of a single byte... Correct?Shanell
char in C is a byte, but a byte in C in not necessarily 8 bits - it is at least 8 bits. uint8_t, in C, is exactly 8 bits. See #18577322Grazier
(see above comment also.) By definition LE and BE have opposite order. But there are subtle possibilities in this BE/LE business including platforms that use mixed endian. I suspect mixed endian is obsolete today and has a doubtful future of returning. Note: Some applications need to swap bit order with various serial communications.Grazier
So the size of a single byte is independently defined by the compiler and by the CPU architecture (which always refers to a byte as an 8-bit unit)? Strange... I thought that compilers defining 16-bit bytes would be compilers designated for an underlying architecture of the same "type".Shanell
C maintains an earlier definition of byte which is a collections of bits, 8 certainly being common. Current literature (CPU manuals) predominately refer to a byte as exactly 8 bits. "compiler defining 16-bit bytes --> same "type" is certainly true in general, but a CPU's native integer bit width and the C code using it is not obliged to match. Obviously an 8-bit processor can not have a C compliant 8-bit int as int must range at least -327676 to 32767. Like-wise, a 64-bit processor could still have C code where the int is 32-bits.Grazier

© 2022 - 2024 — McMap. All rights reserved.