Извлечь Spooky-хэш 128-битное значение из 2 значений uint64 - PullRequest
2 голосов
/ 23 апреля 2019

Я реализую Spooky-хэш в одном из приложений, которые я создаю.

Я ссылаюсь на библиотеки Golang и C .Они обеспечивают вывод int в виде 2-х беззнаковых 64-битных целых чисел.

При рассмотрении реализации python (которая является оберткой в ​​C ++), они получают 128-значное число и даютназад ответ.

Моя проблема в том, что Python делает со значениями 2 64uint, чтобы получить это число?

Я думаю, что это соответствующий код C ++ (из оболочки Python), где онвызывает исходную библиотеку C ++:

static PyObject *
spooky_hash128(PyObject *self, PyObject *args, PyObject *kwargs)
{
    const char *message;
    int message_length;
    uint64 seed[2] = {0};

static char *kwlist[] = {(char *)"message", (char *)"seed",
    NULL};

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|K", kwlist,
    &message, &message_length, &seed)) {
    return NULL;
}

seed[1] = seed[0];

SpookyHash::Hash128(message, message_length, &seed[0], &seed[1]);

PyObject *retval = _PyLong_FromByteArray((unsigned char *)seed, 16, 1, 0);
    return retval;
}

Таким образом, для такой строки, как

15496-17156-0228-a1c731ea-289b-dcf3-a5d8-afb9b6ba34609-5aba2fe5-54ff-098e-c0eb-457

правильные 2 64 единицы: 12579423875165067478 и 12351582206331609335

Целочисленное значение Python 128: 227846475865583962700201584165695002838

Но как 128-битное целое число получено из 2 64 uints? Любые указатели будут полезны для понимания этого.

Ответы [ 3 ]

2 голосов
/ 23 апреля 2019

Выполняет арифметические операции, необходимые для получения числа 128 бит из 2 64 бит :

  • Сдвиг 1 ст (наиболее значимый) один 64 бит слева
  • Добавить 2 nd один

Другими словами, он объединяетих.

Пример (обратите внимание, что вы перечислили числа в обратном порядке):

>>> ui64_0 = 12579423875165067478
>>> ui64_1 = 12351582206331609335
>>>
>>> ui128_0 = (ui64_1 << 64) + ui64_0
>>> ui128_0
227846475865583962700201584165695002838

Это возможно, поскольку Python целые числа не ограничены(или лучше: ограничено наибольшим доступным фрагментом памяти), как [Python 3.Docs]: Числовые типы - int, float, complex состояния:

Целые числа имеют неограниченную точность.

2 голосов
/ 23 апреля 2019

Код использует неподдерживаемую функцию из Python C-API , чтобы взять произвольный массив unsigned char и преобразовать его в целое число. Из определения _PyLong_FromByteArray() видно, почему код вызова включает приведение от uint64[] до char[]:

PyObject *
_PyLong_FromByteArray(const unsigned char* bytes, size_t n,
int little_endian, int is_signed)

Таким образом, вместо двух 64-битных чисел передаются 16 8-битных чисел, для чего предназначен (unsigned char *) cast. Вызов проходит в 16 для n, а little_endian устанавливается на 1 и is_signed на 0.

В коде Python вы можете сделать то же самое с int.to_bytes() методом ; преобразовать оба байта длиной 8 с прямым порядком байтов (поскольку эталонная реализация SpookyHash C ++ явно предназначена для 64-разрядных архитектур с прямым порядком байтов):

>>> bytevalue = (12579423875165067478).to_bytes(8, 'little') + (12351582206331609335).to_bytes(8, 'little')
>>> bytevalue
b'\xd6\x18H\xa6]\x17\x93\xae\xf7`n>\x93\xa2i\xab'
>>> list(bytevalue)
[214, 24, 72, 166, 93, 23, 147, 174, 247, 96, 110, 62, 147, 162, 105, 171]

Каждый байт является компонентом конечного числа, кратным степени 256. Младший значащий байт умножается на 256 ** 0, следующий на 256 ** 1 и т. Д. В системе с прямым порядком байтов самый младший число идет первым (то есть 256 к значению степени 0), и в приведенном выше значении 171 справа является наиболее значимым, будучи 171 от 256 до степени 15.

Вы можете воссоздать число в коде Python, выполнив это самостоятельно:

value = 0
for i, b in enumerate(bytevalue):
    value += b * (256 ** i)

, который выдает ожидаемый результат:

>>> bytevalue = (12579423875165067478).to_bytes(8, 'little') + (12351582206331609335).to_bytes(8, 'little')
>>> for i, b in enumerate(bytevalue):
...     value += b * (256 ** i)
...
>>> value
227846475865583962700201584165695002838

за исключением процессоров, использующих битовое смещение для достижения этого; сдвиг значения на 8 бит влево - это то же самое, что умножение его на 256, и повторное применение таких сдвигов умножит значение на степень 256. Если вы начали с старшего байта и продолжали сдвигать значение так, -дальше влево на 8 битов перед включением следующего байта (используя побитовое ИЛИ) вы получите такой же вывод:

>>> value = 0
>>> for b in reversed(bytevalue):
...     value = value << 8 | b
...
>>> value
227846475865583962700201584165695002838

Чтобы избежать реверса, вы можете сдвинуть текущий байт на количество битов, уже накопленных до объединения:

>>> accumbits = 0
>>> for b in bytevalue:
...     value |= (b << accumbits)
...     accumbits += 8
...
>>> value
227846475865583962700201584165695002838

Вот что на самом деле использует _PyLong_FromByteArray Реализация. Однако внутренняя структура значения Python int фактически разбивает большие целые числа на несколько 30-битных или 15-битных «кусков», поэтому произвольно большие целочисленные значения могут быть помещены в целые числа C фиксированного размера, поэтому функция также использует дополнительное тестирование и сдвиги с PyLong_SHIFT.

Все это сводится к двум 64-битным входным значениям, помещаемым сквозным в память для формирования длинного 128-битного числа; первое число (наименее значимое) справа от второго числа (более значимое), поэтому в коде Python вы можете просто сдвинуть второе число на 64 бита влево и прикрепить результат к первому:

>>> 12579423875165067478 | 12351582206331609335 << 64
227846475865583962700201584165695002838
1 голос
/ 23 апреля 2019

Преобразуйте эти числа в шестнадцатеричные, и вы увидите соединение:

12579423875165067478 = AE93175DA64818D6h
12351582206331609335 = AB69A2933E6E60F7h

227846475865583962700201584165695002838 = AB69A2933E6E60F7AE93175DA64818D6h

Давайте посмотрим на это более подробно:

227846475865583962700201584165695002838 = AB69A2933E6E60F7 AE93175DA64818D6h

Это 128-битное число просто делится на два 64-битных значения.

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