Converting Endianess on a bit field structure

11,475

Solution 1

To get this going I finally got a solution (some what derived from epatel's solution above). This is if I convert from x86 to Solaris SPARC.

We need to first swap the incoming sturcture and then read the elements in reverse order. Basically after looking at how the structures are alligned I saw that the endianess changed both in byte ordering and bit ordering. Here is a pseudo code.

struct orig
{    
    unsigned int    b1:1;
    unsigned int    b2:8;
    unsigned int    b3:7;
    unsigned int    b4:8;
    unsigned int    b5:7;
    unsigned int    b6:1;
};

struct temp
{    
    unsigned int    b6:1;
    unsigned int    b5:7;
    unsigned int    b4:8;
    unsigned int    b3:7;
    unsigned int    b2:8;
    unsigned int    b1:1;
}temp;


func (struct orig *toconvert)
{
    struct temp temp_val;
    //Swap the bytes
    swap32byte((u32*)toconvert);
    //Now read the structure in reverse order - bytes have been swapped
    (u32*)&temp_val = (u32 *)toconvert;
    //Write it back to orignal structure
    toconvert->b6=temp_val.b6;
    toconvert->b5=temp_val.b5;
    toconvert->b4=temp_val.b4;
    toconvert->b3=temp_val.b3;
    toconvert->b2=temp_val.b2;
    toconvert->b1=temp_val.b1;

}

After some experimenting I found that this approach is only valid if the elements completely fill the structure, i.e. there are no unused bits.

Solution 2

You could use a 32 bit integer, and extract information out of it using and- and bitshift operators. With that in place, you could simply use htonl (host-to-network, long). Network byte order is big endian.

This won't be as elegant as a bit-field, but at least you'll know what you have and won't have to worry about the compiler padding your structures.

Solution 3

Processor endianness is unrelated to bit field ordering. It's quite possible to have two compilers on the same computer use opposite ordering for bitfields. So, given this:

union {
    unsigned char x;
    struct {
        unsigned char b1 : 1;
        unsigned char b2 : 7;
    };
} abc;
abc.x = 0;
abc.b1 = 1;
printf( "%02x\n", abc.x );

Unless you happen to have detailed documentation, the only way to know whether that will print out 01 or 80 is to try it.

Solution 4

In a project porting code from MIPS to Linux/x86 we did like this.

struct {

#ifdef __ONE_ENDIANESS__
    unsigned int    b1:1;
    unsigned int    b2:8;
    unsigned int    b3:7;
    unsigned int    b4:8;
    unsigned int    b5:7;
    unsigned int    b6:1;
#define _STRUCT_FILLED
#endif /* __ONE_ENDIANESS__ */

#ifdef __OTHER_ENDIANESS__
    unsigned int    b6:1;
    unsigned int    b5:7;
    unsigned int    b4:8;
    unsigned int    b3:7;
    unsigned int    b2:8;
    unsigned int    b1:1;
#define _STRUCT_FILLED
#endif /* __OTHER_ENDIANESS__ */

};

#ifndef _STRUCT_FILLED
#  error Endianess uncertain for struct
#else
#  undef _STRUCT_FILLED
#endif /* _STRUCT_FILLED */

The macros __ONE_ENDIANESS__ and __OTHER_ENDIANESS__ was the appropriate for the compiler we used so you might need to look into which is appropriate for you...

Solution 5

You have two 16 bit sections there (the first three fields and the last three fields are 16 bits).

That's only 65536 entries. So have a lookup table that holds the bit-reversed version of the fields. Wrap the struct in a union with another struct that has two 16 bit fields to make this easier?

Something like (untested, I'm not near a C compiler):

union u {
    struct {
        unsigned int    b1:1;
        unsigned int    b2:8;
        unsigned int    b3:7;
        unsigned int    b4:8;
        unsigned int    b5:7;
        unsigned int    b6:1;
     } bits;
     struct {
        uint16 first;
        uint16 second;
     } words
} ;

unit16 lookup[65536];

/* swap architectures */

void swapbits ( union u *p)
{
   p->words.first = lookup[p->words.first];
   p->words.second = lookup[p->words.second];
}

Population of the lookup table left as an exercise for the reader :)

However, read your compiler doc carefully. I'm not sure if the C standard requires that struct to fit in a word (although I'd expect most compilers to do that).

Share:
11,475
foo
Author by

foo

Updated on July 21, 2022

Comments

  • foo
    foo almost 2 years

    I need to convert a bit-field structure from little-endian to big-endia architecture. What is the best way to do that, as there will be issues in byte boundaries, if I simply swap the structure elements.

    Ex Structure is:

    struct {
        unsigned int    b1:1;
        unsigned int    b2:8;
        unsigned int    b3:7;
        unsigned int    b4:8;
        unsigned int    b5:7;
        unsigned int    b6:1;
    }; 
    
  • RBerteig
    RBerteig about 15 years
    Notice that fields b2 and b5 span more than one byte in the first example, so it isn't likely that they can be re-written to match in the second case. Otherwise, this trick can save a lot of hair pulling.
  • unwind
    unwind about 15 years
    @epatel: surely sizeof (int) is 4, or some small value like that? The unit for sizeof is chars, so for a 32-bit int on a machine where CHAR_BIT is 8, it will give 4.
  • Alexander Aleksandrovič Klimov
    Alexander Aleksandrovič Klimov about 15 years
    As far as I can tell, the ANSI C standard does not specify the order in which bitfields are allocated inside a byte (or word) so swapping bytes may not be enough.
  • mouviciel
    mouviciel about 15 years
    +1 For me, htonl() or htons() combined with bit masks and bit shifts is the most maintainable approach for this kind of stuff.
  • Jitendra Vyas
    Jitendra Vyas about 15 years
    if a field was longer than 8 bits it also should be swapped for one of the endianess archs
  • Roger
    Roger about 15 years
    yes, won't be portable. but i guess most compilers should put the bits in the "natural" place (e.g. struct {unsigned char a:1,b:6,c:1} ---> a bit 0, b bit 1-6, c bit 7.)...if portability is at prime, use roe's advice.
  • foo
    foo about 15 years
    @epatel This seems to be the best way out for me !
  • foo
    foo about 15 years
    Yes you are correct, though the epatel's method as given below also works, I just need to see where all it will not work :)
  • mouviciel
    mouviciel about 15 years
    The method given by epatel is very common also (and I upvoted it as well). But it can be tricky when bit fields overlap a byte boundary.
  • jbenet
    jbenet about 13 years
    oftentimes packet headers (where physical layout is extremely important) use bit fields to define sub-byte fields. Take for instance, the linux kernel's IP header in netinet/ip.h (or lxr.linux.no/linux+v2.6.38/include/linux/ip.h#L80)
  • zvrba
    zvrba about 13 years
    Um, so what? They're coding for a specific compiler (gcc).
  • Qix - MONICA WAS MISTREATED
    Qix - MONICA WAS MISTREATED over 11 years
    @epatel: sizeof(int) == 32 on 32-bit archs. It is 64 on most 64-bit archs.
  • Qix - MONICA WAS MISTREATED
    Qix - MONICA WAS MISTREATED over 11 years
    Keep in mind this is windows only. Along with that, you have to include all of the winsock libs (ws2_32.lib) which can add an unreasonable amount of overhead to your project.
  • Qix - MONICA WAS MISTREATED
    Qix - MONICA WAS MISTREATED over 11 years
    This doesn't answer the OP's question; the OP will have taken this into account.
  • Bear
    Bear over 7 years
    I'm sure this is downvoted because it's a bad idea, but I landed on this page because I'm unfamiliar with bit fields and I don't know why it's a bad idea. Could someone please explain?