2017-05-05 25 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

相關問題