2017-05-05 26 views
9

我使用ctypes连接到外部库。这个库返回给我一个二进制缓冲区。接口看起来是这样的:将外部提供的缓冲区复制到字节的最有效方法是什么

int getBuff(unsigned char **buf, int *len); 

图书馆还出口释放器,这样我可以释放缓冲区时,我用它做,但是这方面不存在问题给我,所以我不认为我们需要覆盖它。

在我的ctypes代码中,我将buf参数表示为c_void_p。我想尽可能有效地将这个缓冲区复制到一个字节对象中。

目前我有:

data = bytes(bytearray(ctypes.cast(buf, ctypes.POINTER(ctypes.c_ubyte*len.value))[0])) 

其中bufc_void_plenc_int

据我了解,它执行两个副本。一次到bytearray对象,然后再次到bytes对象。

我怎样才能做到这一点只有一个副本?

我目前的工作主要集中在Python 2上,但在适当的时候,我也需要为Python 3提供支持。

+3

在Python 3上,您应该能够删除'bytearray'调用。 – user2357112

+1

为什么你在使用'c_void_p'来铸造而不是'buf = POINTER(c_char)'?然后'getBuff(byref(buf),byref(len))'和'data = buf [:len.value]'。 – eryksun

+0

@eryksun:嗯。你可以切片ctypes指针?新闻给我。 – user2357112

回答

5

显然你可以切片ctypes指针。不是c_void_p,c_char_pc_wchar_p,但POINTER类型的工作。对于POINTER(c_char),切片它给你bytes

data = ctypes.POINTER(ctypes.c_char).from_buffer(buf)[:len.value] 

感谢eryksun为使这件事。另外,还不清楚为什么bufc_void_p而不是已经是POINTER(c_char)。 (对于POINTER(c_char),代码将只是buf[:len.value]。)


对于从支持缓冲协议的一般对象获取bytesmemoryview(...).tobytes()涉及比bytes(bytearray(...))少一个拷贝:

data = memoryview(ctypes.cast(buf, ctypes.POINTER(ctypes.c_ubyte*len.value))[0]).tobytes() 

这是兼容Python 2和Python 3.


请记住,buf这里需要是指向缓冲区的指针,而不是指向缓冲区的指针。 getBuff需要一个指针指针(很可能byref(buf))。

+0

谢谢。是的,双指针是让库可以返回指向调用者的指针。但是在我的ctypes代码中,buf是指向缓冲区的指针。 –

+0

@eryksun:我看了一下,哇,“cast”实际上是一个FFI调用,而不是常规的内置或Python函数,实际上它的速度实在太慢了。在环境中每次调用超过一微秒时,我仅为“cast”调用进行测试,而指针分割大约为67纳秒,memoryview(x).tobytes()大约为285纳秒,元素测试数组)。 – user2357112

+0

@eryksun:你的意思是'ctypes.POINTER(ctypes.c_char)',还是有一些API古怪,这意味着我们实际上应该使用'ctypes.POINTER(ctypes.c_char_p)'? – user2357112

相关问题