数据在c和python之间被损坏

时间:2019-04-15 13:38:40

标签: python cython ctypes

我正在尝试使用Cython和ctypes来使用Python调用c库函数。 但是数据字节以某种方式损坏。有人可以帮忙找到问题吗?

testCRC.c

#include <stdio.h>
unsigned char GetCalculatedCrc(const unsigned char* stream){
   printf("Stream is %x %x %x %x %x %x  %x\n",stream[0],stream[1],stream[2],stream[3],stream[4],stream[5],stream[6]);
   unsigned char dummy=0;
   return dummy;  
}

wrapped.pyx

# Exposes a c function to python
def c_GetCalculatedCrc(const unsigned char*  stream):
     return GetCalculatedCrc(stream)

test.py

  x_ba=(ctypes.c_ubyte *7)(*[0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41]) 
  x_ca=(ctypes.c_char * len(x_ba)).from_buffer(x_ba)
  y=c_GetCalculatedCrc(x_ca.value)

输出:

  

流是d3 ff f7 7f 0 0 5f#预期   0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41

解决方案:

1。 我不得不将cython更新为0.29,以修复不允许使用键入的内存的错误。(只读问题)。

2。 它通过x_ca.raw起作用。但是,当传递x_ca.value时,它会引发错误“访问权限超出限制”。

根据@ead和@DavidW的建议:

´.pyx´:

def c_GetCalculatedCrc(const unsigned char[:]  stream):
    # Exposes a c function to python
    print "received %s\n" %stream[6]
    return GetCalculatedCrc(&stream[0])

“ test.py”:

x_ba=(ctypes.c_ubyte *8)(*[0x47,0xD3,0xFF,0xF7,0x7F,0x00,0x00,0x41])
x_ca=(ctypes.c_char * len(x_ba)).from_buffer(x_ba)
y=c_GetCalculatedCrc(x_ca.raw)

输出:

  

流是47 d3 ff f7 7f 0 0 41

1 个答案:

答案 0 :(得分:3)

@DavidW指出,问题是您使用x_ca.value:调用x_ca.value时,每次创建新的字节对象(请参见documentation)并且内存为复制:

x_ca.value is x_ca.value
#False -> every time a new object is created

但是,在复制内存时,它将\0字符作为字符串的结尾(对于C字符串来说是典型的),如source code所示:

static PyObject *
CharArray_get_value(CDataObject *self, void *Py_UNUSED(ignored))
{
    Py_ssize_t i;
    char *ptr = self->b_ptr;
    for (i = 0; i < self->b_size; ++i)
        if (*ptr++ == '\0')
            break;
    return PyBytes_FromStringAndSize(self->b_ptr, i);
}

因此,x_ca.value的结果是一个字节4的字节对象,当您访问{{1时,它不与x_ca共享内存}}会导致未定义的行为-可能发生任何事情(也是崩溃)。


那该怎么办?

通常,stream[6]函数中不能包含指针参数,但是def是一个例外-char *对象可以自动转换为bytes ,但这不是通过缓冲区协议而是通过PyBytes_AsStringAndSize来实现的。

这就是为什么您不能按原样将char *传递给x_ca的原因:c_GetCalculatedCrc实现了缓冲区协议,但不是x_ca对象,因此没有bytes

另一种方法是使用类型化的内存视图,该视图利用了缓冲协议,即

PyBytes_AsStringAndSize

,现在以原始长度/内容直接传递%%cython def c_GetCalculatedCrc(const unsigned char[:] stream): print(stream[6]);

x_ca

另一种选择是将c_GetCalculatedCrc(x_ca) # 65 as expected 传递给以x_ca.raw为参数的函数,正如@DavidW在评论中指出的那样,该函数与const unsigned char *共享内存。但是,我更喜欢类型化的内存视图-它们比原始指针更安全,并且不会遇到意外的未定义行为。