Как преобразовать целочисленные кратные целочисленные значения в числа с прямым порядком байтов из числа с прямым порядком байтов? - PullRequest
1 голос
/ 29 сентября 2019

Я пытаюсь исправить эту часть ненужной программы , потому что Мне не удалось найти альтернативную программу .

Как вы можете посмотреть данныеиз команд PUSH находятся в неправильном порядке, тогда как Ethereum является машиной с прямым порядком байтов (адрес правильно представлен, потому что они используют меньший тип).
Альтернативой является запуск porosity.exe --code '0x61004b60026319e44e32' --disassm

The u256 тип определяется как

using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

Вот минимальный пример для воспроизведения ошибки :

#include <sstream>
#include <iostream>
#include <iomanip>
#include <boost/multiprecision/cpp_int.hpp>

using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

int main() {
    std::stringstream stream;
    u256 data=0xFEDEFA;
    for (int i = 0; i<5; ++i) { // print only the first 5 digits
        uint8_t dataByte = int(data & 0xFF);
        data >>= 8;
        stream << std::setfill('0') << std::setw(sizeof(char) * 2) << std::hex << int(dataByte) << "  ";
    }
    std::cout << stream.str();
}

Таким образом, числа преобразуются в строку с пробелом между каждымбайт (и только первые байты).

Но затем я столкнулся с проблемой порядка байтов: байты были напечатаны в обратном порядке.Я имею в виду, например, 31722 написано 8a 02 02 на моей машине и 02 02 8a при компиляции для цели с прямым порядком байтов.

Так как я не знаю, какую функцию буста вызывать, я изменил код :

#include <sstream>
#include <iostream>
#include <iomanip>
#include <boost/multiprecision/cpp_int.hpp>

using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

int main() {
    std::stringstream stream;
    u256 data=0xFEDEFA;
    for (int i = 0; i<5; ++i) {
        uint8_t dataByte = int(data >> ((32 - i - 1) * 8));
        stream << std::setfill('0') << std::setw(sizeof(char) * 2) << std::hex << int(dataByte) << "  ";
    }
    std::cout << stream.str();
}

Теперь, почему мои 256-битные целые числа печатаются в основном как серии 00 00 00 00 00?

1 Ответ

1 голос
/ 29 сентября 2019

Кстати, это не проблема порядка байтов;вы не делаете байтовый доступ к объектному представлению.Вы работаете с ним как с 256-битным целым числом и просто запрашиваете младшие 8 бит за раз с data & 0xFF.

Если вы знали, что порядковый номер целевой реализации C и порядок данных известныобъекта boost, вы можете эффективно зациклить его в нисходящем порядке адресов с помощью unsigned char*.

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


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

for (outer loop) {
    uint64_t chunk = data >> (64*3);  // grab the highest 64-bit chunk
    data <<= 64;   // and shift everything up
    // alternative: maybe keep a shift-count in a variable instead of modifying `data`

    // Then pick apart the chunk into its component bytes, in MSB first order
    for (int = 0 ; i<8 ; i++) {
        unsigned tmp = (chunk >> 56) & 0xFF;
        // do something with it
        chunk <<= 8;                   // bring the next byte to the top
    }
}

Во внутреннем цикле более эффективно, чем использование двух смен, можно использовать вращение , чтобы привести старший байт к низу (для & 0xFF) одновременно со смещением младших байтов вверх. Лучшие практики для операций кругового сдвига (поворота) в C ++

Во внешнем цикле IDK, если boost::multiprecision::number, имеет какие-либо API для эффективной индексации встроенных блоков;если это так, то это, вероятно, более эффективно.

Я использовал вложенные циклы, потому что я предполагаю, что data <<= 8 не компилируется особенно эффективно, и ни один не будет (data >> (256-8)) & 0xFF.Но именно так вы можете брать байты сверху, а не снизу.


Другой вариант - стандартный прием для преобразования чисел в строки: сохранение символов в буфере по убываниюзаказ .256-битное (32-байтовое) число займет 64 шестнадцатеричных числа, и вам нужно еще 32 байта между ними.

Например:

  // 97 = 32 * 2 + 32, plus 1 byte for an implicit-length C string terminator
  // plus another 1 for an extra space
  char buf[98];            // small enough to use automatic storage
  char *outp = buf+96;     // pointer to the end
  *outp = 0;               // terminator
  const char *hex_lut = "0123456789abcdef";

  for (int i=0 ; i<32 ; i++) {
      uint8_t byte = data & 0xFF;
      *--outp = hex_lut[byte >> 4];
      *--outp = hex_lut[byte & 0xF];
      *--outp = ' ';
      data >>= 8;
  }
  // outp points at an extra ' '
  outp++;
  // outp points at the first byte of a string like  "12 ab cd"
  stream << outp;

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


Если вас интересует эффективное преобразование в гекс для 8, 16 или 32 байтовданных сразу, см. Как преобразовать число в шестнадцатеричное? для некоторых x86 SIMD-способов .Asm должен легко портировать на встроенные функции C ++.(Вы можете использовать SIMD-тасования для обработки размещения байтов в MSB-первом порядке печати после загрузки из целых чисел с прямым порядком байтов.)

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


Ошибка в коде, который вы добавили:

Поэтому я добавил этот код перед циклом выше:

  for(unsigned int i=0,data,_data;i<33;++i)

unsigned i, data, _data объявляет новые переменные типа unsigned int, которые затеняют предыдущие объявления data и _data.Этот цикл имеет нулевой эффект для data или _data вне области действия цикла.(И содержит UB, потому что вы читаете _data и data без их инициализации.)

Если эти переменные на самом деле все еще являются u256 переменными внешней области видимости, я не вижу очевидной проблемыкроме эффективности, но, возможно, я тоже упускаю очевидное.Я выглядел не очень усердно, потому что использование 64x 256-битных сдвигов и 32x OR кажется ужасной идеей.Это возможно , которое может полностью оптимизироваться, или bswap инструкций обратного байта на ISA, которые их имеют, но я сомневаюсь в этом.Особенно из-за дополнительного усложнения функций оболочки boost::multiprecision::number.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...