Преобразование порядка байтов в структуре битовых полей - PullRequest
7 голосов
/ 07 апреля 2009

Мне нужно преобразовать структуру битового поля из архитектуры с прямым порядком байтов в архитектуру с прямым порядком байтов. Каков наилучший способ сделать это, так как будут проблемы с границами байтов, если я просто поменяю местами элементы структуры.

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

Ответы [ 8 ]

8 голосов
/ 07 апреля 2009

Вы можете использовать 32-разрядное целое число и извлекать из него информацию, используя операторы and- и bithift. Имея это в виду, вы можете просто использовать htonl (host-to-network, long). Порядок байтов в сети с прямым порядком байтов.

Это будет не так элегантно, как битовое поле, но по крайней мере вы будете знать, что у вас есть, и вам не придется беспокоиться о компиляторе, дополняющем ваши структуры.

6 голосов
/ 25 февраля 2011

Порядковый номер процессора не связан с упорядочением битовых полей. Вполне возможно, что два компилятора на одном компьютере будут использовать противоположный порядок для битовых полей. Итак, учитывая это:

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

Если у вас нет подробной документации, единственный способ узнать, распечатает ли это 01 или 80, - это попробовать.

5 голосов
/ 07 апреля 2009

В проекте переноса кода из 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 */

Макросы __ONE_ENDIANESS__ и __OTHER_ENDIANESS__ подходили для компилятора, который мы использовали, поэтому вам может понадобиться посмотреть, какой вам подходит ...

1 голос
/ 07 апреля 2009

Вы хотите сделать это между каналом (файлом или сетью) и вашей структурой. Моя предпочтительная практика состоит в том, чтобы изолировать файловый ввод-вывод от структур путем записи кода, который строит файловые буферы в известном представлении, и сопоставления кода чтения, который обращает это преобразование.

Ваш конкретный пример особенно сложен для догадки, поскольку битовые поля определены как unsigned int, а sizeof(unsigned int) особенно непереносим.

Если предположить, что sizeof(int)==4 в качестве SWAG, то получение указателя на структуру и запись отдельных байтов, вероятно, даст вам нужный ответ.

Трюк определения структуры по-разному для разных платформ может работать, но в приведенном вами примере нет чистого разрыва на границах байтов, поэтому вряд ли возможно создать эквивалент одной платформы в другой без разделения одного или нескольких полей на две части.

1 голос
/ 07 апреля 2009

У вас есть две 16-битные секции (первые три поля и последние три поля 16-битные).

Это всего 65536 записей. Так что имейте таблицу поиска, которая содержит версию полей с инвертированным битом. Оберните структуру в объединение с другой структурой, которая имеет два 16-битных поля, чтобы сделать это проще?

Что-то вроде (не проверено, я не рядом с компилятором 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];
}

Заполнение таблицы поиска, оставленной в качестве упражнения для читателя:)

Однако внимательно прочитайте документацию по вашему компилятору. Я не уверен, требует ли стандарт C, чтобы эта структура вписывалась в слово (хотя я бы ожидал, что это сделает большинство компиляторов).

0 голосов
/ 10 апреля 2009

Для этого я наконец-то нашел решение (кое-что полученное из решения эпателя выше). Это если я конвертирую из x86 в Solaris SPARC.

Нам нужно сначала поменять местами входящую структуру, а затем прочитать элементы в обратном порядке. В основном, посмотрев на выравнивание структур, я увидел, что порядок байтов изменился как в порядке следования байтов, так и в порядке следования битов. Вот псевдокод.

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;

}

После некоторых экспериментов я обнаружил, что этот подход действителен, только если элементы полностью заполняют структуру, т. Е. Нет неиспользованных битов.

0 голосов
/ 08 апреля 2009

Не следует использовать битовые поля, когда важен физический макет, поскольку он определяется реализацией, в которой порядок заполнения большего слова.

0 голосов
/ 07 апреля 2009

Этого должно быть достаточно, чтобы поменять местами байты. Битовая позиция в байте одинакова для старшего и младшего байтов.
например :

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];
...