Home > Software design >  How to reverse endianness?
How to reverse endianness?

Time:09-24

Can someone help me understand this code?

int reverse_endianess(int value) {
    int resultat = 0;
    char *source, *destination;
    int i;

    source = (char *) &value;
    destination = ((char *) &resultat)   sizeof(int);
    for (i = 0; i < sizeof(int); i  )
        *(--destination) = *(source  );
    return resultat;
}

I can't understand this part of code:

destination = ((char *) &resultat)   sizeof(int);
for (i = 0; i < sizeof(int); i  )
    *(--destination) = *(source  );

CodePudding user response:

The following causes destination to point to the byte that follows resultat (as long as resultat is an int):

destination = ((char *) &resultat)   sizeof(int);

It could also have been written as follows:

destination = (char *)(&resultat   1);

The following is just a simple memory copy loop:

for (i = 0; i < sizeof(int); i  )
        *(--destination) = *(source  );

It's equivalent to the following:

for (i = 0; i < sizeof(int); i  ) {
   --destination;             // Point to the one byte earlier.
   *destination = *source;    // Copy one byte.
   source  ;                  // Point to one byte later.
}

Program flow (assuming 32-bit int and 8-bit char)

After setup:

source               value
 ----------           --- --- --- --- 
|        -------     | a | b | c | d |
 ----------     |     --- --- --- --- 
                |      ^
                 ------ 

destination          resultat 
 ----------           --- --- --- --- 
|        -------     | 0 | 0 | 0 | 0 |
 ----------     |     --- --- --- --- 
                |                      ^
                 ---------------------- 

After one pass of the loop:

source               value
 ----------           --- --- --- --- 
|        -------     | a | b | c | d |
 ----------     |     --- --- --- --- 
                |          ^
                 ---------- 

destination          resultat 
 ----------           --- --- --- --- 
|        -------     | 0 | 0 | 0 | a |
 ----------     |     --- --- --- --- 
                |                  ^
                 ------------------ 

When it's done:

source               value
 ----------           --- --- --- --- 
|        -------     | a | b | c | d |
 ----------     |     --- --- --- --- 
                |                      ^
                 ---------------------- 

destination          resultat 
 ----------           --- --- --- --- 
|        -------     | d | c | b | a |
 ----------     |     --- --- --- --- 
                |      ^
                 ------ 

CodePudding user response:

Three different approaches. The first one is most efficient on the systems having byte reversing instructions.

#define SWAPUC(a,b) do{unsigned char temp = (a); (a) = (b); (b) = temp;}while(0)

int reverse(int i)
{
    unsigned int val = i;

    if(sizeof(val) == 4)
        val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24);
    if(sizeof(val) == 8)
        val = ((val & 0x00000000000000ffULL) << 56) | ((val & 0xff00000000000000ULL) >> 56) |
              ((val & 0x000000000000ff00ULL) << 40) | ((val & 0x00ff000000000000ULL) >> 40) |
              ((val & 0x0000000000ff0000ULL) << 24) | ((val & 0x0000ff0000000000ULL) >> 24) |
              ((val & 0x00000000ff000000ULL) <<  8) | ((val & 0x000000ff00000000ULL) >> 8);
    
    return val;
}

int  reverse1(int val)
{
    union
    {
        unsigned i;
        unsigned char uc[sizeof(val)];
    }uni = {.i = val};

    if(sizeof(val) == 8)
    {
        SWAPUC(uni.uc[7], uni.uc[0]);
        SWAPUC(uni.uc[6], uni.uc[1]);
        SWAPUC(uni.uc[5], uni.uc[2]);
        SWAPUC(uni.uc[4], uni.uc[3]);
    }
    if(sizeof(val) == 4)
    {
        SWAPUC(uni.uc[3], uni.uc[0]);
        SWAPUC(uni.uc[2], uni.uc[1]);
    }
    return uni.i;
}

int reverse2(int val)
{
    unsigned char uc[sizeof(val)];
    memcpy(uc, &val, sizeof(uc));
    if(sizeof(val) == 8)
    {
        SWAPUC(uc[7], uc[0]);
        SWAPUC(uc[6], uc[1]);
        SWAPUC(uc[5], uc[2]);
        SWAPUC(uc[4], uc[3]);
    }
    if(sizeof(val) == 4)
    {
        SWAPUC(uc[3], uc[0]);
        SWAPUC(uc[2], uc[1]);
    }
    memcpy(&val, uc, sizeof(uc));
    return val;
}


int main(void)
{
    printf("%x\n", reverse2(0xaabbccdd));
}

The generated code (x86):

reverse:
        mov     eax, edi
        bswap   eax
        ret
reverse1:
        mov     eax, edi
        xor     edx, edx
        mov     ecx, edi
        shr     eax, 24
        movzx   esi, ch
        sal     ecx, 24
        mov     dl, al
        mov     eax, edi
        sal     esi, 16
        shr     eax, 16
        mov     dh, al
        movzx   eax, dx
        or      eax, esi
        or      eax, ecx
        ret
reverse2:
        mov     eax, edi
        xor     edx, edx
        mov     ecx, edi
        shr     eax, 24
        movzx   esi, ch
        sal     ecx, 24
        mov     dl, al
        mov     eax, edi
        sal     esi, 16
        shr     eax, 16
        mov     dh, al
        movzx   eax, dx
        or      eax, esi
        or      eax, ecx
        ret
.LC0:
        .string "%x\n"

Or cortex M4 (this one has byte swapping instruction)

reverse:
        rev     r0, r0
        bx      lr
reverse1:
        mov     r3, r0
        lsrs    r2, r3, #24
        movs    r0, #0
        bfi     r0, r2, #0, #8
        ubfx    r2, r3, #16, #8
        bfi     r0, r2, #8, #8
        ubfx    r2, r3, #8, #8
        bfi     r0, r2, #16, #8
        bfi     r0, r3, #24, #8
        bx      lr
reverse2:
        mov     r3, r0
        lsrs    r2, r3, #24
        movs    r0, #0
        bfi     r0, r2, #0, #8
        ubfx    r2, r3, #16, #8
        bfi     r0, r2, #8, #8
        ubfx    r2, r3, #8, #8
        bfi     r0, r2, #16, #8
        bfi     r0, r3, #24, #8
        bx      lr
.LC0:

So the winner is the first function using only the bitwise arithmetics.

CodePudding user response:

Lets say sizeof(int) = 4 so 32 bit int. A char is sizeof 1, 1 byte. A normal int* looks like this:

aabbccdd // the int in hexadecimal
^ pointer points to start

If we cast it to a char*, we get aa. Thats whats done with source.

If we now add sizeof 4, we jump 4 bytes to the right:

aabbccdd?? 
                  ^

We are now one byte behind the value, accessing this may segfault the program or just read garbage. This does not happen due to the use of --destination instead of destination--. It is decremented first. Now we just read the integer passed in from the front, while writing it from the back:

a1b2c3d4 // original int
->

d4c3b2a1 // destination
              <-

Note that two hexadecimal digits are one byte, which is why we dont get 4d3c2b1a. We leave the bytes in the correct way, but put the first bytes last.

CodePudding user response:

I was using this for a very long time.
data is a pointer to value to be reversed
n is the number of char to be reversed; usually 2, 4, 8 for short, int, long long.
But this can be different on various architectures/OS

void SwapEndianN(char *data, unsigned short n) {
  unsigned short k; char c;
  for ( k=0 ; k < (n/2) ;k   ) {
    c = *(data ((n-1)-k));
    *(data ((n-1)-k)) = *(data k);
    *(data k) = c;
  }
}
  • Related