Как преобразовать двоичный буфер C в его шестнадцатеричное представление в строке Python? - PullRequest
2 голосов
/ 28 октября 2019

Хорошо известно, что pysha3 несовместим с pypy, и, поскольку он не поддерживается в течение 3 лет, я должен изменить его сам.

Конечно, правильным способом было бы выполнить полное переписывание в чистом видеPython-код (который также приведет к более быстрой реализации по сравнению с текущим), но мне не хватает необходимых знаний как в криптографии, так и в фоновой математике, чтобы сделать это, и программа, использующая его, очень интенсивно использует список (для которого требуется python3 безgil для многопоточности или python3 с jit).

Единственная точка отказа сводится к этой функции , которая должна вызываться кодом C:

static PyObject*
_Py_strhex(const char* argbuf, const Py_ssize_t arglen)
{
    static const char *hexdigits = "0123456789abcdef";

    PyObject *retval;
#if PY_MAJOR_VERSION >= 3
    Py_UCS1 *retbuf;
#else
    char *retbuf;
#endif
    Py_ssize_t i, j;

    assert(arglen >= 0);
    if (arglen > PY_SSIZE_T_MAX / 2)
        return PyErr_NoMemory();

#if PY_MAJOR_VERSION >= 3
    retval = PyUnicode_New(arglen * 2, 127);
    if (!retval)
            return NULL;
    retbuf = PyUnicode_1BYTE_DATA(retval);
#else
    retval = PyString_FromStringAndSize(NULL, arglen * 2);
    if (!retval)
            return NULL;
    retbuf = PyString_AsString(retval);
    if (!retbuf) {
            Py_DECREF(retval);
            return NULL;
    }
#endif
    /* make hex version of string, taken from shamodule.c */
    for (i=j=0; i < arglen; i++) {
        unsigned char c;
        c = (argbuf[i] >> 4) & 0xf;
        retbuf[j++] = hexdigits[c];
        c = argbuf[i] & 0xf;
        retbuf[j++] = hexdigits[c];
    }

    return retval;
}

Уровень совместимости для Cython составляет 3,2 для Pypy, и PyUnicode_New был введен в Python3.3.

Я попытался исправить это путем замены всего файла следующим кодом Cython:

cdef Py_strhex(const char* argbuf, const Py_ssize_t arglen):
    return (argbuf[:arglen]).hex()

но, похоже, это вызывает ошибку сегментации, включая компиляцию и использование официальной реализации Python. И используя официальный двоичный файл PyPy, у меня нет символов отладки для gdb, поэтому я не знаю почему.

(gdb) bt
#0  0x00007ffff564cd00 in pypy_g_text_w__pypy_interpreter_baseobjspace_W_Root () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#1  0x00007ffff5d721a8 in pypy_g_getattr () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#2  0x00007ffff543a8bd in pypy_g_dispatcher_15 () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#3  0x00007ffff5ab909b in pypy_g_wrapper_second_level.star_2_14 () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#4  0x00007fffd7212372 in _Py_strhex.2738 () from /usr/lib64/pypy3.6-v7.2.0-linux64/site-packages/pysha3-1.0.3.dev1-py3.6-linux-x86_64.egg/_pysha3.pypy3-72-x86_64-linux-gnu.so
#5  0x00007fffd7217990 in _sha3_sha3_224_hexdigest_impl.2958 () from /usr/lib64/pypy3.6-v7.2.0-linux64/site-packages/pysha3-1.0.3.dev1-py3.6-linux-x86_64.egg/_pysha3.pypy3-72-x86_64-linux-gnu.so
#6  0x00007ffff5be2170 in pypy_g_generic_cpy_call__StdObjSpaceConst_funcPtr_SomeI_5 () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#7  0x00007ffff54b25cd in pypy_g.call_1 () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#8  0x00007ffff56715b9 in pypy_g_BuiltinCodePassThroughArguments1_funcrun_obj () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#9  0x00007ffff56ffc06 in pypy_g_call_valuestack__AccessDirect_None () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so
#10 0x00007ffff5edb29b in pypy_g_CALL_METHOD__AccessDirect_star_1 () from /usr/lib64/pypy3.6-v7.2.0-linux64/bin/libpypy3-c.so

Увеличение глубины стека Linux по умолчанию до 65 МБ не меняет глубину рекурсиигде происходит ошибка по умолчанию, даже если глубина стека превышает 200, похоже, это не связано с переполнением стека.

Ответы [ 2 ]

1 голос
/ 28 октября 2019

С точки зрения Cython, это проще, чем вы думаете:

cdef Py_strhex(const char* argbuf, const Py_ssize_t arglen):
    return (argbuf[:arglen]).hex()

По сути вам не нужно malloc (что в любом случае приводило к утечке памяти, поскольку в ней отсутствовала free) и вам не нужно memcpy. argbuf[:arglen] создает bytes объект соответствующей длины (делает копию данных).

Это определенно работает на CPython. На PyPy2 он выдает AttributeError: 'str' object has no attribute 'hex', что правильно для Python 2. Я бы подумал, что если он вызовет ошибку сегментации, это произойдет до AttributeError, так что это многообещающе. У меня нет готового PyPy3 ...


Редактировать :

Теперь мне удалось протестировать мой код на PyPy3 следующим образом:

# extra Cython code just to call the function
def test():
    cdef const char* a = "0123456789"
    return Py_strhex(a,10)

Затем из Python:

import modulename
modulename.test()

Это прекрасно работает без ошибки сегментации ;поэтому я вполне уверен, что этот код в порядке.

Я не знаю, как вы вызываете код Cython, поскольку вы не говорите;однако Cython не генерирует код C с намерением просто скопировать отдельную функцию. Он генерирует модуль, и модуль ожидает его импорта (некоторые вещи настраиваются во время импорта модуля). В частности, Cython устанавливает таблицу строк во время инициализации модуля, включая строку "hex", используемую для поиска атрибута. Чтобы правильно использовать этот код , вам нужно убедиться, что модуль, в котором он содержится, сначала импортируется , а не просто сохранять копию сгенерированного кода Cython в файле C. Это немного сложно в Python 3 и, вероятно, не соответствует вашим целям.

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

0 голосов
/ 29 октября 2019

Хорошо, я нашел то, что искал, используя этот вариант. Это не будет работать на всех компиляторах и совместимо только с Python3, но обеспечивает частичную совместимость с PyPy (некоторые тесты, которые, как предполагается, терпят неудачу, успешно выполняются, потому что возвращается неверный хэш) с pysha3 вместе с программами, от которых зависит:

static PyObject * _Py_strhex(const char* argbuf, const Py_ssize_t arglen) {
    static const char *hexdigits = "0123456789abcdef";

    assert(arglen >= 0);

    if (arglen > PY_SSIZE_T_MAX / 2)
        return PyErr_NoMemory();

    const Py_ssize_t len=arglen*2;
    char retbuf[len+1];
    retbuf[len+1]=0;

    /* make hex version of string, taken from shamodule.c */
    for (Py_ssize_t i=0,j=0; i < arglen; i++) {
        retbuf[j++] = hexdigits[(argbuf[i] >> 4) & 0xf];
        retbuf[j++] = hexdigits[argbuf[i] & 0xf];
    }

    return PyUnicode_FromStringAndSize(retbuf,len);
}
...