如何将C二进制缓冲区转换为它’;Python字符串中的十六进制表示法?
众所周知,pysha3与pypy不兼容,因为它已经3年没有维护了,所以我不得不自己修改它 当然,一个正确的方法是用纯python代码执行完全重写(这也会导致比当前代码更快的实现),但我缺乏做这件事所需的密码学和背景数学知识,并且使用它的程序非常需要列表(需要一个不带gil的python3进行多线程处理,或者需要一个带jit的python3) 必须由C代码调用的单点故障:如何将C二进制缓冲区转换为它’;Python字符串中的十六进制表示法?,python,c,python-3.x,cython,pypy,Python,C,Python 3.x,Cython,Pypy,众所周知,pysha3与pypy不兼容,因为它已经3年没有维护了,所以我不得不自己修改它 当然,一个正确的方法是用纯python代码执行完全重写(这也会导致比当前代码更快的实现),但我缺乏做这件事所需的密码学和背景数学知识,并且使用它的程序非常需要列表(需要一个不带gil的python3进行多线程处理,或者需要一个带jit的python3) 必须由C代码调用的单点故障: static PyObject* _Py_strhex(const char* argbuf, const Py_ssize_
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;
}
但是它似乎触发了一个分段错误,包括编译和使用官方的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不会改变发生segfault的递归深度,因此,即使堆栈深度大于200,这似乎与堆栈溢出无关。就Cython而言,它比您想象的要简单:
cdef Py_strhex(const char* argbuf, const Py_ssize_t arglen):
return (argbuf[:arglen]).hex()
基本上,您不需要malloc
(这会导致内存泄漏,因为它缺少一个free
),也不需要memcpy
argbuf[:arglen]
创建一个具有适当长度的字节
对象(制作数据副本)
这在CPython上确实有效。在PyPy2上,它生成了AttributeError:“str”对象没有属性“hex”
,这对于Python2来说是正确的。我可以想象,如果它产生分段错误,它会发生在AttributeError
之前,所以这很有希望。我没有现成的PyPy3
编辑: 我现在已经在Py3上测试了我的代码,如下所示:
# 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”
。要正确使用此代码,而不是将generate Cython代码的副本转储到C文件中。在Python 3中这样做有点复杂,可能不适合您的目的
我将此答案保留为当前状态,因为我相信它是正确的,并且问题发生在您未指定的部分。很可能它对您没有用处,您可以随意忽略它。确定找到了我使用此变体所要查找的内容。 这不会在所有编译器上都起作用,并且仅与Python3兼容,但它会在其所依赖的程序上与pysha3实现部分PyPy兼容性(某些测试可能会失败,因为返回了错误的哈希值):
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);
}
static PyObject*\u Py\u strhex(const char*argbuf,const Py\u ssize\u t arglen){
静态常量字符*hexdigits=“0123456789abcdef”;
断言(arglen>=0);
如果(arglen>PY\u SSIZE\u T\u MAX/2)
返回PyErr_nomery();
常数Py_ssize_t len=arglen*2;
char-retbuf[len+1];
retbuf[len+1]=0;
/*制作字符串的十六进制版本,取自shamodule.c*/
对于(Py_ssize_t i=0,j=0;i>4)和0xf];
retbuf[j++]=十六进制数字[argbuf[i]&0xf];
}
从stringandsize(retbuf,len)返回PyUnicode_;
}
有没有可能将您的代码库更新到Python 3.6或更高版本?@Selcuk pypypy支持Python 3.6,但仅在Python级别。在C级别,它的兼容性仍处于3.2级别。即使是最新版本的,也缺少运行pysha3的函数。对不起,我说的是使用Python 3.6附带的内置hashlib.sha3
。@Selcuk它是为了实现sha3的keccak变体,以便与以太坊兼容,所以不幸的是它不兼容。项目本身不使用pysha3:它被许多pip依赖项使用,所以解决这个问题会更简单。我不知道你为什么说在C级它仍然处于3.2级。也许它错过了一个特定的API乐趣但是如果您报告它,我们会修复这个bug。如果argbuf被传递到堆栈,这不会失败吗(因为垃圾收集器稍后会尝试释放它) ? 当然,我使用的是PyPy3的最新版本。但正如您在回溯中看到的,SEGFULT的发生是因为试图调用Interpeter来查找要执行的函数。字符串拥有自己的内存。这会创建一个临时字符串,其中包含argbuf
(可以安全地超过argbuf
,但在这种情况下不需要)。从它派生的十六进制字符串也是独立的,并且拥有自己的内存。从内存管理的角度来看,只要调用函数时argbuf
有效就可以了。恐怕这只是试图回答问题“如何编写Cython函数将const char*
转换为hex”是问题的一部分。我真的无法诊断pypypy内部问题!正如回溯所示,如果在不调用python getattr(这也意味着不使用Cython)的情况下使用完整的C语言,听起来像是真正的问题.我的意思是用与Cpython 3.2兼容的全C语言编写(我不知道如何实现)。如果Cython代码仍然产生seg错误,那么我想我帮不了你;我会很快删除我的答案。没有可能仍然可以编辑它。我可以做的是使用C字符串转换为十六进制,然后转换为python字符串。这归结为一个问题:如何使用C
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);
}