Как я преобразовываю структуру с обратным порядком байтов в небольшую структуру порядка байтов?

На основе https://www.wowza.com/docs/getting-started-with-wowza-player#create-a-custom-wowza-player-with-wowza-player-builder Есть небольшой набор функций, которые можно настроить, изменив встроенный тег

8 ответов

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

Изменить: если структура была записана без упаковки, тогда это должно быть довольно просто. Что-то вроде этого (непроверенного) кода должен сработать:

// Functions to swap the endian of 16 and 32 bit values

inline void SwapEndian(UINT16 &val)
{
    val = (val<<8) | (val>>8);
}

inline void SwapEndian(UINT32 &val)
{
    val = (val<<24) | ((val<<8) & 0x00ff0000) |
          ((val>>8) & 0x0000ff00) | (val>>24);
}

Затем, как только вы загрузили структуру, просто замените каждый элемент:

SwapEndian(r.foo);
SwapEndian(r.bar);
SwapEndian(r.baz);
13
ответ дан 30 November 2019 в 07:39
поделиться

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

PS. Тем не менее, может быть машина со странным порядком байтов. То, что я только что сказал, применимо к большинству используемых машин (x86, ARM, PowerPC, SPARC).

3
ответ дан 30 November 2019 в 07:39
поделиться

Вы должны исправить порядок байтов каждого члена, состоящего из более чем одного байта, индивидуально. Строки не нужно преобразовывать (fooword и barword), так как их можно рассматривать как последовательности байтов.

Однако вы должны позаботиться о другой проблеме: о принадлежности членов в вашей структуре. По сути, вы должны проверить, является ли sizeof (RECORD) одинаковым для кода unix и windows. Компиляторы обычно предоставляют прагмы для определения желаемого алигента (например, #pragma pack).

1
ответ дан 30 November 2019 в 07:39
поделиться

Фактически, порядок байтов - это свойство базового оборудования, а не ОС.

Лучшее решение - преобразовать в стандарт при записи данных - Google для "сетевого порядка байтов" и вы должны найти способы сделать это.

Изменить: вот ссылка: http://www.gnu.org/software/hello/manual/libc/Byte-Order.html

10
ответ дан 30 November 2019 в 07:39
поделиться

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

  • Как программа UNIX записывает в файл
  • Если это двоичная копия объекта, точная структура структуры.
  • Если это двоичная копия, то какой порядок байтов -

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

1
ответ дан 30 November 2019 в 07:39
поделиться

Мне нравится реализовывать метод SwapBytes для каждого типа данных, который требует замены, например:

inline u_int ByteSwap(u_int in)
{
    u_int out;
    char *indata = (char *)&in;
    char *outdata = (char *)&out;
    outdata[0] = indata[3] ;
    outdata[3] = indata[0] ;

    outdata[1] = indata[2] ;
    outdata[2] = indata[1] ;
    return out;
}

inline u_short ByteSwap(u_short in)
{
    u_short out;
    char *indata = (char *)&in;
    char *outdata = (char *)&out;
    outdata[0] = indata[1] ;
    outdata[1] = indata[0] ;
    return out;
}

Затем я добавляю функцию в структуру, которая требует подкачки, например:

struct RECORD {
  UINT32 foo;
  UINT32 bar;
  CHAR fooword[11];
  CHAR barword[11];
  UNIT16 baz;
  void SwapBytes()
  {
    foo = ByteSwap(foo);
    bar = ByteSwap(bar);
    baz = ByteSwap(baz);
  }
}

Затем вы можете изменить свой код, который читает (или записывает) структуру следующим образом:

fstream f;
f.open("file.bin", ios::in | ios::binary);

RECORD r;

f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();

cout << "fooword = " << r.fooword << endl;

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

1
ответ дан 30 November 2019 в 07:39
поделиться

Что-то вроде этого должно работать:

#include <algorithm>

struct RECORD {
    UINT32 foo;
    UINT32 bar;
    CHAR fooword[11];
    CHAR barword[11];
    UINT16 baz;
}

void ReverseBytes( void *start, int size )
{
    char *beg = start;
    char *end = beg + size;

    std::reverse( beg, end );
}

int main() {
    fstream f;
    f.open( "file.bin", ios::in | ios::binary );

    // for each entry {
    RECORD r;
    f.read( (char *)&r, sizeof( RECORD ) );
    ReverseBytes( r.foo, sizeof( UINT32 ) );
    ReverseBytes( r.bar, sizeof( UINT32 ) );
    ReverseBytes( r.baz, sizeof( UINT16 )
    // }

    return 0;
}
0
ответ дан 30 November 2019 в 07:39
поделиться

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

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

char buffer[32];
char* temp = buffer;  

f.read(buffer, 32);  

RECORD rec;
rec.foo = parse_uint32(temp); temp += 4;
rec.bar = parse_uint32(temp); temp += 4;
memcpy(&rec.fooword, temp, 11); temp += 11;
memcpy(%red.barword, temp, 11); temp += 11;
rec.baz = parse_uint16(temp); temp += 2;

Объявление parse_uint32 будет выглядеть так:

uint32 parse_uint32(char* buffer)
{
  uint32 x;
  // ...
  return x;
}

Это очень простая абстракция, она не ' На практике обновление указателя требует дополнительных затрат:

uint32 parse_uint32(char*& buffer)
{
  uint32 x;
  // ...
  buffer += 4;
  return x;
}

Более поздняя форма позволяет более чистый код для синтаксического анализа буфера; указатель автоматически обновляется при синтаксическом анализе ввода.

Точно так же у memcpy может быть помощник, например:

void parse_copy(void* dest, char*& buffer, size_t size)
{
  memcpy(dest, buffer, size);
  buffer += size;
}

Прелесть такого типа организации в том, что у вас может быть пространство имен "little_endian" и "big_endian", а затем вы можете сделать это в своем коде:

using little_endian;
// do your parsing for little_endian input stream here..

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

НЕ абстрагируйте это в класс с виртуальными методами; просто добавит накладных расходов, но не стесняйтесь, если хотите:

little_endian_reader reader(data, size);
uint32 x = reader.read_uint32();
uint32 y = reader.read_uint32();

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

Обратите внимание, как выбор порядка байтов здесь был сделан во ВРЕМЯ КОМПИЛЯЦИИ (поскольку мы создаем объект little_endian_reader), поэтому мы вызываем накладные расходы виртуального метода без особых на то причин, поэтому я бы не пошел с таким подходом. ; -)

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

Я имею в виду, это выглядит сложным?

uint32 xsize = buffer.read<uint32>();
uint32 ysize = buffer.read<uint32>();
float aspect = buffer.read<float>();    

Код может выглядеть так красиво и с минимальными накладными расходами! Если порядок байтов одинаков для файла и архитектуры, для которой скомпилирован код, внутренний цикл может выглядеть следующим образом:

uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;

Это может быть незаконным на некоторых архитектурах, поэтому оптимизация может быть плохой идеей, и используйте более медленный, но более надежный подход:

uint32 value = ptr[0] | (static_cast<uint32>(ptr[1]) << 8) | ...; ptr += 4;
return value;

На x86, который может компилироваться в bswap или mov, что имеет достаточно низкие накладные расходы, если метод встроен; компилятор вставит узел "перемещения" в промежуточный код, ничего больше, что довольно эффективно. Если выравнивание представляет собой проблему, полная последовательность чтения-сдвига или может быть сгенерирована с ошибкой, но все же не слишком потрепанной. Compare-branch может позволить оптимизацию, если протестировать LSB адреса и посмотреть, можно ли использовать быструю или медленную версию синтаксического анализа. Но это означало бы штраф за тест при каждом чтении.

О, да, мы читаем ЗАГОЛОВКИ и прочее, я не думаю, что это узкое место для слишком многих приложений. Если какой-то кодек выполняет действительно ПЛОТНЫЙ внутренний цикл, опять же, рекомендуется чтение во временный буфер и декодирование оттуда. Тот же принцип ... никто не читает из файла побайтно при обработке большого объема данных. Ну, на самом деле, я видел такой код очень часто, и обычный ответ на "зачем вы это делаете" состоит в том, что файловые системы выполняют блочное чтение и что байты все равно берутся из памяти, правда, но они проходят через глубокий стек вызовов что требует больших накладных расходов для получения нескольких байтов!

Тем не менее, напишите код парсера один раз и используйте миллионы раз -> эпическая победа.

Чтение непосредственно в структуру из файла: НЕ ДЕЛАЙТЕ ЭТО ШУМА!

5
ответ дан 30 November 2019 в 07:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: