python — работа с порядком байтов в переполнении стека

Я работаю над переводом системы с Python на C ++. Мне нужно иметь возможность выполнять действия в C ++, которые обычно выполняются с использованием Python struct.unpack (интерпретировать двоичные строки как числовые значения). Для целочисленных значений я могу заставить это (вроде) работать, используя типы данных в stdint.h:

struct.unpack("i", str) ==> *(int32_t*) str; //str is a char* containing the data

Это работает правильно для двоичных строк с прямым порядком байтов, но не работает с двоичными строками с прямым порядком байтов. По сути, мне нужен эквивалент использования > тег в struct.unpack:

struct.unpack(">i", str) ==> ???

Пожалуйста, обратите внимание, если есть лучший способ сделать это, я весь слух. Однако я не могу использовать c ++ 11 и любые сторонние библиотеки, кроме Boost. Я также должен быть в состоянии интерпретировать числа с плавающей и двойной, как в struct.unpack(">f", str) а также struct.unpack(">d", str), но я доберусь до этого, когда решу это.

НОТА Я должен указать, что порядковый номер моей машины не имеет значения в этом случае. Я знаю, что поток битов, который я получаю в своем коде, ВСЕГДА будет с прямым порядком байтов, и поэтому мне нужно решение, которое всегда будет охватывать случай с прямым порядком байтов. Статья, на которую указывает BoBTFish в комментариях, кажется, предлагает решение.

3

Решение

Распаковывайте строку по одному байту за раз.

unsigned char *str;
unsigned int result;

result =  *str++ << 24;
result |= *str++ << 16;
result |= *str++ << 8;
result |= *str++;
4

Другие решения

Для 32 и 16-битных значений:

Это как раз та проблема, которая у вас есть для сетевых данных, которая имеет порядок байтов. Вы можете использовать ntohl превратить 32-битный в порядок хостов, в вашем случае little-endian

Функция ntohl () преобразует целое число без знака netlong из сетевого байта в
порядок байтов хоста.

int res = ntohl(*((int32_t) str)));

Это также позаботится о случае, когда ваш хост находится в порядке байтов и ничего не будет делать.

Для 64-битных значений

Нестандартно на Linux / BSD вы можете посмотреть на 64-битный ntohl () в C ++?, что указывает на htobe64

Эти функции преобразуют байтовую кодировку целочисленных значений из порядка байтов, который
текущий процессор («хост») использует байты с прямым порядком байтов и байтов с обратным порядком байтов
порядок.

Для окон попробуйте: Как я могу преобразовать между значениями с прямым порядком байтов и значениями с прямым порядком байтов в C ++?

Что указывает на _byteswap_uint64 а также 16- и 32-разрядное решение и специфичный для gcc вызов __builtin_bswap (32/64).

Другие размеры

Большинство систем не имеют значений, которые не имеют длину 16/32/64 бита. В этот момент я мог бы попытаться сохранить его в 64-битном значении, сдвинуть его, и они переведут. Я бы написал несколько хороших тестов. Я подозреваю, что это необычная ситуация, и более подробная информация поможет.

7

Во-первых, актерский состав, который вы делаете:

char *str = ...;
int32_t i = *(int32_t*)str;

приводит к неопределенному поведению из-за строгого правила псевдонимов (если str инициализируется чем-то вроде int32_t x; char *str = (char*)&x;). С практической точки зрения это приведение может привести к не выровненному чтению, которое вызывает ошибку шины (сбой) на некоторых платформах и снижает производительность на других.

Вместо этого вы должны делать что-то вроде:

int32_t i;
std::memcpy(&i, c, sizeof(i));

Существует ряд функций для замены байтов между собственным порядком байтов хоста и независимым от хоста порядком: ntoh*(), hton*(), где * ничего l, или же s для различных поддерживаемых типов. Поскольку разные хосты могут иметь разные порядки байтов, это может быть то, что вы хотите использовать, если данные, которые вы читаете, используют согласованную сериализованную форму на всех платформах.

ntoh(i);

Вы также можете вручную перемещать байты в str прежде чем копировать его в целое число.

std::swap(str[0],str[3]);
std::swap(str[1],str[2]);
std::memcpy(&i,str,sizeof(i));

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

std::memcpy(&i,str,sizeof(i));
i = (i&0xFFFF0000)>>16 | (i&0x0000FFFF)<<16;
i = (i&0xFF00FF00)>>8  | (i&0x00FF00FF)<<8;
2

Это попадает в сферу мелкого блуждания.

for (i=0;i<sizeof(struct foo);i++) dst[i] = src[i ^ mask];

где маска == (размер типа -1), если сохраненный и нативный порядковый номер различаются.

С помощью этой техники можно преобразовать структуру в битовые маски:

 struct foo {
byte a,b;       //  mask = 0,0
short e;        //  mask = 1,1
int g;          //  mask = 3,3,3,3,
double i;       //  mask = 7,7,7,7,7,7,7,7
} s; // notice that all units must be aligned according their native size

Снова эти маски могут быть закодированы с двумя битами на символ: (1<<n)-1Это означает, что на 64-битных машинах можно кодировать необходимые маски структуры размером 32 байта в одну константу (с выравниванием в 1,2,4 и 8 байтов).

unsigned int mask = 0xffffaa50;  // or zero if the endianness matches
for (i=0;i<16;i++) {
dst[i]=src[i ^ ((1<<(mask & 3))-1]; mask>>=2;
}
0

Если ваши полученные значения являются действительно строками (char * или std :: string) и вы знаете их форматную информацию, то sscanf () и atoi (), ну, действительно, ato () будут вашими друзьями. Они берут хорошо отформатированные строки и преобразуют их в переданные форматы (вид обратного printf).

-1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector