如何将 C 二进制缓冲区转换为 Python 字符串中的十六进制表示形式?
How to convert a C binary buffer to it’s hex representation in Python string?
众所周知pysha3不兼容pypy,而且3年无人维护,只好自己修改
当然,正确的方法是用纯 python 代码执行完全重写(这也会导致比当前代码更快的实现),但我缺乏密码学方面的必要知识和背景数学来做到这一点,并且使用它的程序非常非常密集(这需要 python3 没有 gil 用于多线程或 python3 带有 jit)。
C代码必须调用的单点故障boils down to this function:
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;
}
pypy 的 cython 兼容性级别为 3.2,PyUnicode_New
在 python3.3 中引入。
我尝试用 hammer 方法修复它,用以下 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 堆栈深度增加到 65Mb 不会改变发生段错误的递归深度,因此即使堆栈深度大于 200,这似乎与堆栈溢出。
就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 在模块初始化期间设置了 table 个字符串,包括用于查找属性的字符串 "hex"
。要正确使用此代码 you'd need to ensure the module it's contained in is imported first 而不是将生成的 Cython 代码的副本转储到 C 文件中。在 Python 3 中这样做有点复杂,可能不适合您的目的。
我将保留此答案的当前状态,因为我相信它是正确的,并且问题出现在您未指定的部分。它很可能对您没有用,您可以随意忽略它。
好的,使用此变体找到了我要找的东西。
这不适用于所有编译器,仅与 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);
}
众所周知pysha3不兼容pypy,而且3年无人维护,只好自己修改
当然,正确的方法是用纯 python 代码执行完全重写(这也会导致比当前代码更快的实现),但我缺乏密码学方面的必要知识和背景数学来做到这一点,并且使用它的程序非常非常密集(这需要 python3 没有 gil 用于多线程或 python3 带有 jit)。
C代码必须调用的单点故障boils down to this function:
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;
}
pypy 的 cython 兼容性级别为 3.2,PyUnicode_New
在 python3.3 中引入。
我尝试用 hammer 方法修复它,用以下 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 堆栈深度增加到 65Mb 不会改变发生段错误的递归深度,因此即使堆栈深度大于 200,这似乎与堆栈溢出。
就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 在模块初始化期间设置了 table 个字符串,包括用于查找属性的字符串 "hex"
。要正确使用此代码 you'd need to ensure the module it's contained in is imported first 而不是将生成的 Cython 代码的副本转储到 C 文件中。在 Python 3 中这样做有点复杂,可能不适合您的目的。
我将保留此答案的当前状态,因为我相信它是正确的,并且问题出现在您未指定的部分。它很可能对您没有用,您可以随意忽略它。
好的,使用此变体找到了我要找的东西。 这不适用于所有编译器,仅与 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);
}