Изменить порядковый номер всей структуры в C ++ - PullRequest
0 голосов
/ 28 августа 2018

Я пишу синтаксический анализатор в C ++ для анализа хорошо определенного двоичного файла. Я объявил все необходимые структуры. И так как мне интересны только определенные поля, поэтому в своих структурах я пропустил необязательные поля, создав массив размером с символ, равный пропущенным байтам. Так что я просто читаю файл в массиве char и приводю указатель char к моему указателю структуры. Теперь проблема состоит в том, что все поля данных в этом двоичном файле находятся в порядке с прямым порядком байтов, поэтому после приведения типов мне нужно изменить порядковый номер всех полей структуры. Один из способов - сделать это вручную для каждого поля. Но существуют различные структуры со многими полями, поэтому будет очень сложно сделать это вручную. Так что это лучший способ достичь этого. И так как я буду разбирать очень большие такие файлы (скажем, в ТБ), мне нужен быстрый способ сделать это.

РЕДАКТИРОВАТЬ: я использую атрибут (упакован) , поэтому не нужно беспокоиться о заполнении.

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Если вы можете составить список смещений (в байтах относительно верхней части файла) полей, для которых требуется преобразование в порядке байтов, а также размер этих полей, то вы можете сделать все endian-преобразование с одним циклом for, непосредственно в массиве char. Например. как то так (псевдокод):

struct EndianRecord {
   size_t offsetFromTop;
   size_t fieldSizeInByes;
};

std::vector<EndianRecord> todoList;
// [populate the todo list here...]

char * rawData = [pointer to the raw data]
for (size_t i=0; i<todoList.size(); i++)
{
   const EndianRecord & er = todoList[i];
   ByteSwap(&rawData[er.offsetFromTop], er.fieldSizeBytes);
}

struct MyPackedStruct * data = (struct MyPackedStruct *) rawData;
// Now you can just read the member variables
// as usual because you know they are already
// in the correct endian-format.

... конечно, сложная часть состоит в правильном todoList, но, поскольку формат файла четко определен, должна быть возможность генерировать его алгоритмически (или, что еще лучше, создать его как генератор с например, метод GetNextEndianRecord(), который вы можете вызвать, чтобы вам не нужно было хранить очень большой вектор в памяти)

0 голосов
/ 28 августа 2018

Один из способов сделать это - объединить препроцессор C с операторами C ++. Напишите пару классов C ++, как этот:

#include "immintrin.h"

class FlippedInt32
{
    int value;
public:
    inline operator int() const
    {
        return _bswap( value );
    }
};

class FlippedInt64
{
    __int64 value;
public:
    inline operator __int64() const
    {
        return _bswap64( value );
    }
};

Тогда

#define int FlippedInt32

перед включением заголовка, который определяет эти структуры. #undef сразу после #include.

Это заменит все int поля в структурах на FlippedInt32, который имеет тот же размер, но возвращает перевернутые байты.

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

0 голосов
/ 28 августа 2018

Если вы можете выполнять смещенные обращения без штрафа и не возражаете против трюков, специфичных для компилятора или платформы, для управления заполнением, это может сработать. (Полагаю, вы согласны с этим, поскольку вы упомянули __attribute__((packed))).

В этом случае самым хорошим подходом является написание упаковщиков значений для ваших необработанных типов данных и использование их вместо необработанного типа при объявлении вашей структуры в первую очередь. Помните, что упаковщик значений должен быть тривиальным / POD-подобным, чтобы это работало. Если у вас есть платформа POSIX, вы можете использовать ntohs/ntohl для преобразования в порядковый номер, вероятно, она будет лучше оптимизирована, чем вы пишете сами.

Если неправильно выровненные обращения запрещены или медленны на вашей платформе, вам нужно вместо этого десериализовать. Поскольку у нас пока нет отражения, вы можете сделать это с теми же обертками значений (плюс заполнитель Ignore<N>, пропускающий N байт для полей, которые вас не интересуют), и объявить их в кортеже вместо структуры - вы может перебрать членов в кортеже и сказать каждому десериализовать себя из сообщения.

...