简体   繁体   English

在位域结构上转换Endianess

[英]Converting Endianess on a bit field structure

I need to convert a bit-field structure from little-endian to big-endia architecture. 我需要将位域结构从little-endian转换为big-endia架构。 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: Ex结构是:

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;
}; 

You could use a 32 bit integer, and extract information out of it using and- and bitshift operators. 您可以使用32位整数,并使用and-和bitshift运算符从中提取信息。 With that in place, you could simply use htonl (host-to-network, long). 有了这个,你可以简单地使用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. 这不会像位字段那样优雅,但至少你会知道你拥有什么,而不必担心编译器填充你的结构。

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. 除非您碰巧有详细的文档,否则了解是否打印出01或80的唯一方法就是尝试它。

In a project porting code from MIPS to Linux/x86 we did like this. 在将代码从MIPS移植到Linux / x86的项目中,我们确实喜欢这样。

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... __ONE_ENDIANESS____OTHER_ENDIANESS__适用于我们使用的编译器,因此您可能需要查看哪个适合您...

You have two 16 bit sections there (the first three fields and the last three fields are 16 bits). 那里有两个16位部分(前三个字段,后三个字段是16位)。

That's only 65536 entries. 这只是65536个条目。 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? 将结构包装在另一个具有两个16位字段的结构的联合中,以使其更容易?

Something like (untested, I'm not near a C compiler): 像(未经测试,我不在C编译器附近):

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). 我不确定C标准是否要求结构符合一个单词(尽管我希望大多数编译器都这样做)。

You want to do this between the channel (file, or network) and your structure. 您希望在通道(文件或网络)和结构之间执行此操作。 My preferred practice is to isolate file I/O from structures by write code that builds the file buffers in a known representation, and matching read code that reverses that transformation. 我的首选做法是通过以已知表示形式构建文件缓冲区的写代码和匹配反转该转换的读取代码来隔离文件I / O与结构。

Your specific example is particularly difficult to guess at because the bitfields are defined to be unsigned int and sizeof(unsigned int) is particularly non-portable. 您的具体示例特别难以猜测,因为位域定义为unsigned int,而sizeof(unsigned int)特别不可移植。

Assuming as a SWAG that sizeof(int)==4 then getting a pointer to the struct and reording the individual bytes probably gets you the answer you want. 假设作为SWAG的sizeof(int)==4然后获取指向结构的指针并重新编写单个字节可能会得到你想要的答案。

The trick of defining the struct differently for different platforms might work, but in the example you cite there isn't a clean break at byte boundaries, so it is not likely to be possible to produce an equivalent of one platform in the other without splitting one or more of the fields into two pieces. 为不同平台定义结构的技巧可能有用,但在示例中,您引用的是字节边界没有干净的中断,因此不可能在另一个平台上生成等效的一个平台而不进行拆分一个或多个字段分成两部分。

It should be enough to swap the bytes. 交换字节应该足够了。 Bit position within a byte is the same in big and little endian. 一个字节内的位位置在大端和小端都是相同的。
eg : 例如:

char* dest = (char*)&yourstruct;
unsigned int orig = yourstruct;
char* origbytes = (char*)&orig;
dest[0] = origbytes[3];
dest[1] = origbytes[2];
dest[2] = origbytes[1];
dest[3] = origbytes[0];

当物理布局很重要时,不应使用位字段,因为它是实现定义的,填充较大的字的顺序。

To get this going I finally got a solution (some what derived from epatel's solution above). 为了实现这一目标,我终于得到了一个解决方案(一些从上面的epatel解决方案中获得的解决方案)。 This is if I convert from x86 to Solaris SPARC. 这是我从x86转换为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, ie there are no unused bits. 经过一些实验后,我发现这种方法只有在元素完全填满结构时才有效,即没有未使用的位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM