2015-01-26 4 views
7

मैं साइथन में एक पायथन 2.7 एक्सटेंशन मॉड्यूल लिख रहा हूं। मैं एक लाइब्रेरी ऑब्जेक्ट कैसे बना सकता हूं जो नए स्टाइल बफर इंटरफेस को कार्यान्वित करता है जो सी लाइब्रेरी द्वारा मुझे दी गई मेमोरी का एक हिस्सा लपेटता है? स्मृति का हिस्सा केवल बाइट्स की एक स्ट्रिंग है, न कि संरचना या बहुआयामी सरणी। मुझे const void * पॉइंटर और लम्बाई दी गई है, और कुछ विवरण यह है कि पॉइंटर कब तक वैध रहता है।साइथन में एक नई शैली बफर ऑब्जेक्ट में सी पॉइंटर और लंबाई को कैसे लपेटें?

मैं स्मृति की प्रतिलिपि नहीं बना सकता-जो मेरे आवेदन के लिए प्रदर्शन को मार देगा।

पुराने स्टाइल बफर ऑब्जेक्ट्स के साथ मैं बस PyBuffer_FromMemory() का उपयोग कर सकता हूं, लेकिन मुझे एक नई शैली बफर ऑब्जेक्ट बनाने के लिए एक आसान तरीका नहीं मिल रहा है।

क्या मुझे अपनी खुद की कक्षा बनाना है जो बफर इंटरफेस लागू करता है? या साइथन इसे करने का एक आसान तरीका प्रदान करता है?

मैंने साइथन दस्तावेज से Unicode and Passing Strings और Typed Memoryviews पृष्ठों को पढ़ा है, लेकिन दस्तावेज़ीकरण कमजोर है और बहुत पूरा नहीं है और ऐसा कोई उदाहरण नहीं है जो मैं करना चाहता हूं।

यहाँ मैं क्या कोशिश की है (test.pyx) है:

from libc.stdlib cimport malloc 
from libc.string cimport memcpy 

## pretend that this function is in some C library and that it does 
## something interesting. (this function is unrelated to the problem 
## I'm experiencing -- this is just an example function that returns a 
## chunk of memory that I want to wrap in an object that follows the 
## new buffer protocol.) 
cdef void dummy_function(const void **p, size_t *l): 
    cdef void *tmp = malloc(17) 
    memcpy(tmp, "some test\0 bytes", 17) 
    p[0] = tmp 
    l[0] = 17 

cpdef getbuf(): 
    cdef const void *cstr 
    cdef size_t l 
    dummy_function(&cstr, &l) 

    ## error: test.pyx:21:20: Invalid base type for memoryview slice: void 
    #cdef const void[:] ret = cstr[:l] 

    ## error: test.pyx:24:9: Assignment to const 'ret' 
    #cdef const char[:] ret = cstr[:l] 

    ## error: test.pyx:27:27: Cannot convert 'void const *' to memoryviewslice 
    #cdef char[:] ret = cstr[:l] 

    ## this next attempt cythonizes, but raises an exception: 
    ## $ python -c 'import test; test.getbuf()' 
    ## Traceback (most recent call last): 
    ## File "<string>", line 1, in <module> 
    ## File "test.pyx", line 15, in test.getbuf (test.c:1411) 
    ## File "test.pyx", line 38, in test.getbuf (test.c:1350) 
    ## File "stringsource", line 614, in View.MemoryView.memoryview_cwrapper (test.c:6763) 
    ## File "stringsource", line 321, in View.MemoryView.memoryview.__cinit__ (test.c:3309) 
    ## BufferError: Object is not writable. 
    cdef char[:] ret = (<const char *>cstr)[:l] 

    ## this raises the same exception as above 
    #cdef char[:] ret = (<char *>cstr)[:l] 

    return ret 
+0

शायद यह असफल रहा है क्योंकि आप 'char *' के बजाय 'const char *' पर कास्टिंग कर रहे हैं? – Kevin

+0

@ केविन: मैंने अपने प्रश्न को यह बताने के लिए अद्यतन किया कि एक ही अपवाद तब भी होता है जब मैं 'const *' के बजाय 'char *' पर जाता हूं। यह बात बताने के लिए धन्यवाद। –

+1

अधिक विस्तार से समस्या का अध्ययन करने के बाद, मैं यह इंगित करना चाहता हूं कि memcpy अवैध है। आपने 'tmp' को कॉन्स के रूप में घोषित किया है, और फिर आपने इसे संशोधित किया है। यह सी मानक द्वारा अपरिभाषित व्यवहार है। चूंकि आपने यह भी कहा है कि आप स्मृति की प्रतिलिपि से बचने की कोशिश कर रहे हैं, मैं इस बिंदु पर थोड़ा उलझन में हूं। – Kevin

उत्तर

5

आप एक extension type कि __getbuffer__ और __releasebuffer__special methods को परिभाषित करते हुए बफर प्रोटोकॉल लागू करता है परिभाषित कर सकते हैं। उदाहरण के लिए:

$ python -c 'import test; print repr(memoryview(test.getbuf()).tobytes())' 
'some test\x00 bytes\x00' 

मैं वहाँ एक आसान तरीका है पता नहीं है:

from cpython.buffer cimport PyBuffer_FillInfo 
from libc.stdlib cimport free, malloc 
from libc.string cimport memcpy 

cdef void dummy_function(const void **p, size_t *l): 
    cdef void *tmp = malloc(17) 
    memcpy(tmp, "some test\0 bytes", 17) 
    p[0] = tmp 
    l[0] = 17 

cdef void free_dummy_data(const void *p, size_t l, void *arg): 
    free(<void *>p) 

cpdef getbuf(): 
    cdef const void *p 
    cdef size_t l 
    dummy_function(&p, &l) 
    return MemBuf_init(p, l, &free_dummy_data, NULL) 

ctypedef void dealloc_callback(const void *p, size_t l, void *arg) 

cdef class MemBuf: 
    cdef const void *p 
    cdef size_t l 
    cdef dealloc_callback *dealloc_cb_p 
    cdef void *dealloc_cb_arg 

    def __getbuffer__(self, Py_buffer *view, int flags): 
     PyBuffer_FillInfo(view, self, <void *>self.p, self.l, 1, flags) 
    def __releasebuffer__(self, Py_buffer *view): 
     pass 

    def __dealloc__(self): 
     if self.dealloc_cb_p != NULL: 
      self.dealloc_cb_p(self.p, self.l, self.dealloc_cb_arg) 

# Call this instead of constructing a MemBuf directly. The __cinit__ 
# and __init__ methods can only take Python objects, so the real 
# constructor is here. See: 
# https://mail.python.org/pipermail/cython-devel/2012-June/002734.html 
cdef MemBuf MemBuf_init(const void *p, size_t l, 
         dealloc_callback *dealloc_cb_p, 
         void *dealloc_cb_arg): 
    cdef MemBuf ret = MemBuf() 
    ret.p = p 
    ret.l = l 
    ret.dealloc_cb_p = dealloc_cb_p 
    ret.dealloc_cb_arg = dealloc_cb_arg 
    return ret 
ऊपर (नाम test.pyx) के साथ

आप निम्नलिखित व्यवहार मिलता है।

+0

'मेमबफ' मेमोरी रिसाव बना रहा है। '__releasebuffer__' को 'PyBuffer_Release (देखें)' कॉल करना चाहिए। आपको शायद 'मेमबफ' के लिए '__dealloc__' फ़ंक्शन लिखना चाहिए जो' फ्री 'कहता है यदि उसके पास सी फ़ंक्शन द्वारा लौटाई गई स्मृति का स्वामित्व है। – Dunes

+0

@Dunes: हाँ, आप सही हैं। मैंने स्मृति को '__dealloc__' में मुक्त करने के लिए अपना उत्तर अपडेट किया। मेरे असली दुनिया कोड में सी फ़ंक्शन मेमोरी ब्लॉक के स्वामित्व को बरकरार रखता है, इसलिए मैंने इस उदाहरण कोड में स्मृति को मुक्त करने के बारे में नहीं सोचा था। –

1

पायथन 3.3 PyMemoryView_FromMemory सी-एपीआई फ़ंक्शन है, जो memoryview पाइथन ऑब्जेक्ट प्रदान किए गए सी बफर से बनाता है। memoryview ऑब्जेक्ट्स वास्तव में नए स्टाइल बफर इंटरफेस को लागू करते हैं।

यदि आप its sources में देखते हैं, तो आप देखेंगे कि वे अपेक्षाकृत सरल हैं। यह PyMemoryView_FromBuffer जैसा ही करता है, पूर्व Py_buffer को PyBuffer_FillInfo के साथ भरता है।

चूंकि बाद वाला पाइथन 2.7 में मौजूद है, तो हम खुद को PyBuffer_FillInfo क्यों नहीं बुला सकते हैं?

from libc.stdlib cimport malloc 
from libc.string cimport memcpy 

cdef extern from "Python.h": 
    ctypedef struct PyObject 
    object PyMemoryView_FromBuffer(Py_buffer *view) 
    int PyBuffer_FillInfo(Py_buffer *view, PyObject *obj, void *buf, Py_ssize_t len, int readonly, int infoflags) 
    enum: 
     PyBUF_FULL_RO 

cdef void dummy_function(const void **p, size_t *l): 
    cdef void *tmp = malloc(17) 
    memcpy(tmp, "some test\0 bytes", 17) 
    p[0] = tmp 
    l[0] = 17 

cpdef getbuf(): 
    cdef const void *cstr 
    cdef size_t l 
    cdef Py_buffer buf_info 
    cdef char[:] ret 
    cdef int readonly 

    dummy_function(&cstr, &l) 

    readonly = 1 
    PyBuffer_FillInfo(&buf_info, NULL, <void*>cstr, l, readonly, PyBUF_FULL_RO) 
    ret = PyMemoryView_FromBuffer(&buf_info) 

    return ret 

ध्यान दें कि, हालांकि, कि वापस लौटाया गया मान रेपर कि इस तरह दिखता है होगा: <MemoryView of 'memoryview' at 0x7f216fc70ad0>। ऐसा इसलिए है क्योंकि साइथन _memoryviewslice के अंदर memoryview को लपेटने लगता है। चूंकि memoryview ऑब्जेक्ट्स पहले से ही बफर इंटरफ़ेस को लागू करते हैं, इसलिए आपको संभवतः PyMemoryView_FromBuffer कॉल का परिणाम तुरंत वापस करना चाहिए।

इसके अतिरिक्त, आप अपने बफर के जीवनकाल के प्रबंधन के लिए ज़िम्मेदार हैं। memoryview इस तरह से बनाए गए ऑब्जेक्ट्स स्वचालित रूप से स्मृति को मुक्त नहीं करेंगे। आपको यह स्वयं करना होगा, यह सुनिश्चित करना कि आप केवल ऐसा करते हैं कि एक बार memorybuffer संदर्भ नहीं देता है। इस संबंध में, रिचर्ड हैंनसेन का जवाब बहुत बेहतर विकल्प है।

संबंधित मुद्दे