Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I create and return an arrayref in Inline::C?

Tags:

perl

I am trying to write a C function via Inline::C that will create and return an array reference to Perl... below is my script and output. Am I allocating the array and populating it properly? In particular, I create a temporary array of SV*'s and pass them to av_make, and return a reference created with newRV_noinc. The reference counts seem fine when I look at the returned array ref with Devel::Peek::Dump, which looks identical to the same data structure created in perl directly.

I do not yet understand what mortalization/sv_2mortal are, or if I need it here. Apparently, Inline::C automatically calls sv_2mortal on functions returning SV*, which may or may not be relevant.

#!/usr/bin/env perl
use strict;
use warnings FATAL => "all";
use 5.010_000;
use Data::Dumper;
use autodie;

use Inline ('C');
use Devel::Peek;

my $from_perl = [0 .. 9];
my $from_c = inline_array_maker(10); #same as above but in C

say Dumper $from_perl;
Dump($from_perl);

say Dumper $from_c;
Dump($from_c);


__END__
__C__
SV* (int len){
    int i;
    SV ** tmp_buffer;
    AV * arr;

    tmp_buffer  = malloc(sizeof(SV*) * len);

    printf("allocating tmp_buffer of size %d\n", len);
    for (i = 0; i < len; i++) {
        tmp_buffer[i] = newSViv(i);
    }

    // is av_make the most efficient way of doing this?  
    printf("av_make\n");
    arr = av_make(len, tmp_buffer);

    printf("freeing tmp_buffer\n");
    for (i = 0; i < len; i++) {
        sv_free(tmp_buffer[i]);
    }
    free(tmp_buffer);

    return newRV_noinc(arr);
}

I get the following output.

allocating tmp_buffer of size 10
av_make
freeing tmp_buffer
$VAR1 = [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9
        ];

SV = IV(0x20c7520) at 0x20c7530
REFCNT = 1
FLAGS = (PADMY,ROK)
RV = 0x21c0fa8
SV = PVAV(0x25c7ec8) at 0x21c0fa8
    REFCNT = 1
    FLAGS = ()
    ARRAY = 0x25a0e80
    FILL = 9
    MAX = 9
    ARYLEN = 0x0
    FLAGS = (REAL)
    Elt No. 0
    SV = IV(0x20b2dd8) at 0x20b2de8
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 0
    Elt No. 1
    SV = IV(0x20b2fb8) at 0x20b2fc8
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 1
    Elt No. 2
    SV = IV(0x20c69f8) at 0x20c6a08
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 2
    Elt No. 3
    SV = IV(0x20c6a10) at 0x20c6a20
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 3
$VAR1 = [
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9
        ];

SV = IV(0x20d25c8) at 0x20d25d8
REFCNT = 1
FLAGS = (PADMY,ROK)
RV = 0x25ac6b8
SV = PVAV(0x25c7ea0) at 0x25ac6b8
    REFCNT = 1
    FLAGS = ()
    ARRAY = 0x25b9140
    FILL = 9
    MAX = 9
    ARYLEN = 0x0
    FLAGS = (REAL)
    Elt No. 0
    SV = IV(0x25aca80) at 0x25aca90
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 0
    Elt No. 1
    SV = IV(0x25ac750) at 0x25ac760
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 1
    Elt No. 2
    SV = IV(0x25ac5e8) at 0x25ac5f8
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 2
    Elt No. 3
    SV = IV(0x25ac930) at 0x25ac940
    REFCNT = 1
    FLAGS = (IOK,pIOK)
    IV = 3
like image 996
user1481 Avatar asked Mar 11 '13 21:03

user1481


2 Answers

Have you read perldoc perlguts? It should get you most of the way there. Inline::C can handle an AV* return value too. I might do something like:

#!/usr/bin/env perl

use strict;
use warnings;

use Inline C => <<'END';
  AV* make_arrayref (int size) {
    int i;
    AV* ret = newAV();
    sv_2mortal((SV*)ret);
    for(i=0; i<size; i++) {
      av_push(ret, newSViv(i) );
    }
    return ret;
  }
END

my $arrayref = make_arrayref(10);
print "$_\n" for @$arrayref;

Here is some code I have written which might help too.

like image 198
Joel Berger Avatar answered Oct 28 '22 14:10

Joel Berger


I do not yet understand what mortalization/sv_2mortal are, or if I need it here. Apparently, Inline::C automatically calls sv_2mortal on functions returning SV*, which may or may not be relevant.

When you allocated the RV, you became the owner of it. If you relinquish your hold on it, you need to decrement its refcount. But that would free it on the spot since noone else holds a reference to it. Mortalising is a delayed refcount decrement. It gives the caller a chance to grab it (and increment its refcount) first.

So yes, the RV needs to be mortalised, and yes, the typemap will mortalise a returned SV* for you.

Your code is bug free, but as Joel Berger showed, you don't need to prebuild a C array. His code is also fine.

He also showed that if your return type is AV*, the typemap will create a (mortal) reference to the array, but it will not mortalise the array itself. Your two primary return options are thus

// Return type = SV*
return newRV_noinc(arr);

and

// Return type = AV*
return MUTABLE_AV(sv_2mortal(MUTABLE_SV(newRV_noinc(arr)));
like image 32
ikegami Avatar answered Oct 28 '22 14:10

ikegami