3

I am trying to create a buffer with a special type in Python, to send it to a C function wrapped with CFFI.

In C, I have something like:

typedef unsigned char UINT8;
typedef UINT8* PUINT8;

Then, in Python, the above lines are in the ffi.cdef() and my buffer instantiation code looks like:

nb_buffer = 8
buffer_size = 42

buffers = ffi.new( "PUINT8[]", nb_buffer )

for i in range( nb_buffer ):
    tmp_buffer = ffi.buffer( ffi.new( "UINT8[]", 1 ), buffer_size )
    buffers[ i ] = ffi.cast( "PUINT8", ffi.from_buffer( tmp_buffer ) )

c.function( buffers )

The C function receives a UINT8**.

And... that ends with a segmentation fault a little bit further, in the C function.

So I print buffers[ i ] after using ffi.cast:

<cdata 'unsigned char *' 0x2cbaca0>
<cdata 'unsigned char *' 0x2cbacd8>
<cdata 'unsigned char *' 0x2cbaca0>
<cdata 'unsigned char *' 0x2cbacd8>
<cdata 'unsigned char *' 0x2cbaca0>
<cdata 'unsigned char *' 0x2cbacd8>
<cdata 'unsigned char *' 0x2cbaca0>
<cdata 'unsigned char *' 0x2cbacd8>

What am I missing? Is the buffer being garbage collected as soon as it overwrites tmp_buffer?

DRz
  • 1,078
  • 1
  • 11
  • 29
  • Not looking in details, but ``ffi.buffer( ffi.new( "UINT8[]", 1 ), ... )`` is basically wrong. It means "allocate an array (of one UINT8); then take a buffer to it; then forget immediately the new array, so that it is immediately freed". – Armin Rigo May 14 '16 at 12:28
  • Also, you allocate arrays of one UINT8 and then consider them as 42-bytes buffers. The last 41 bytes are not allocated anyway. – Armin Rigo May 14 '16 at 12:30
  • I thought the importance of the first parameter of `ffi.buffer()` was the type of the CData, not its length (as it is the second parameter) and that it would somehow create a given-CData-typed buffer of the given size. But I now understand that that second parameter is for slicing a memory size that would be bigger than needed. Thanks Armin! I will try your answer asap. – DRz May 14 '16 at 17:17
  • 1
    `ffi.buffer()` is not allocating any memory, it is merely returning a view over existing memory. – Armin Rigo May 15 '16 at 07:16

2 Answers2

3

How about:

buffers = ffi.new( "PUINT8[]", nb_buffer )
keepalive = []

for i in range( nb_buffer ):
    p = ffi.new("UINT8[]", buffer_size)
    keepalive.append(p)
    buffers[i] = p

c.function( buffers )

# keepalive stays alive at least until here
Armin Rigo
  • 12,048
  • 37
  • 48
0

Using list comprehension:

keepalive = [ ffi.new( "UINT8[]", buffer_size ) for i in range( nb_buffer ) ]
buffers = ffi.new( "PUINT8[]", keepalive )

c.functions( buffers )

# keepalive stays alive at least until here
DRz
  • 1,078
  • 1
  • 11
  • 29