我使用ctypes连接到外部库。这个库给我一个二进制缓冲区。界面如下所示:
int getBuff(unsigned char **buf, int *len);
该库还会导出一个deallocator,以便在完成后可以释放缓冲区,但这方面对我没有任何问题,所以我认为我们不需要覆盖它。
在我的ctypes代码中,我将buf
参数表示为c_void_p
。我想尽可能有效地将此缓冲区复制到字节对象中。
目前我有:
data = bytes(bytearray(ctypes.cast(buf, ctypes.POINTER(ctypes.c_ubyte*len.value))[0]))
其中buf
为c_void_p
而len
为c_int
。
据我了解,这会执行两份副本。一次到bytearray对象,然后再到bytes对象。
如何只使用一份副本?
我目前的工作主要集中在Python 2上,但在适当的时候我也需要为Python 3提供支持。
答案 0 :(得分:6)
显然你可以切片ctypes指针。不是c_void_p
,c_char_p
或c_wchar_p
,但POINTER
类型有效。对于POINTER(c_char)
,对其进行切片会为您提供bytes
:
data = ctypes.POINTER(ctypes.c_char).from_buffer(buf)[:len.value]
感谢eryksun提出这个问题。此外,还不清楚为什么buf
是c_void_p
而不是POINTER(c_char)
。 (对于POINTER(c_char)
,代码只是buf[:len.value]
。)
要从支持缓冲协议的常规对象中获取bytes
,memoryview(...).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)
)。