How to write partial of a buffer which was returned from a C function to a file?

eryk sun eryksun at gmail.com
Thu Apr 12 21:08:18 EDT 2018


On Thu, Apr 12, 2018 at 11:25 PM, Gregory Ewing
<greg.ewing at canterbury.ac.nz> wrote:
>
> To get around this, you may need to declare the return type
> as POINTER(c_char) instead:
>
>> For a general character pointer that may also point to binary data,
>
>> POINTER(c_char) must be used.
>
> I'm not sure where to go from here, though, because the
> ctypes documentation peters out before explaining exactly
> what can be done with a POINTER object.

Pointers can be indexed and sliced. You have to be careful, however,
since there's no bounds checking. Alternatively, without copying, you
can create an array view on the buffer, which is bounded and thus
doesn't risk an access violation (segfault). For example:

Say the function returns a pointer to a buffer with the contents
b"spam\x00". Let's simulate the function result using a void * pointer
to initialize a char * pointer:

    >>> buf0 = ctypes.create_string_buffer(b'spam')
    >>> pvoid = ctypes.c_void_p(ctypes.addressof(buf0))
    >>> result = ctypes.POINTER(ctypes.c_char).from_buffer_copy(pvoid)

This pointer object has just the address of the buffer, without
supporting references in _b_base_ or _objects:

    >>> result._b_base_ is result._objects is None
    True

(In other words, ctypes isn't responsible for the buffer, as simulated
here. Libraries that allocate their own memory for results have to
provide a function to free it. Especially on Windows, you cannot rely
on both Python and the DLL to use the same heap.)

You can slice the pointer:

    >>> result[:5]
    b'spam\x00'

Or you can access the buffer more safely as a new array view:

    >>> array_t = ctypes.c_char * 5
    >>> pointer_t = ctypes.POINTER(array_t)
    >>> result.contents
    c_char(b's')

    >>> buf1 = pointer_t(result.contents)[0]
    >>> buf1[:]
    b'spam\x00'

This buf1 array is a view on the buffer, not a copy. It reflects
whatever changes are made to the underlying buffer:

    >>> buf0[:] = b'eggs\x00'
    >>> buf1[:]
    b'eggs\x00'

As such, ctypes knows it doesn't have to free this memory:

    >>> buf1._b_needsfree_
    0


More information about the Python-list mailing list