Moving binary data to / from Perl using SWIG

I am trying to facilitate the movement of binary data between Perl and my C ++ library.

I created a C ++ structure to pass binary_data data:

struct binary_data { unsigned long length; unsigned char *data; }; 

In my SWIG interface file for me there is the following:

 %typemap(in) binary_data * (binary_data temp) { STRLEN len; unsigned char *outPtr; if(!SvPOK($input)) croak("argument must be a scalar string"); outPtr = (unsigned char*) SvPV($input, len); printf("set binary_data '%s' [%d] (0x%X)\n", outPtr, len, $input); temp.data = outPtr; temp.length = len; $1 = &temp; } %typemap(out) binary_data * { SV *obj = sv_newmortal(); if ($1 != 0 && $1->data != 0 && $1->length > 0) { sv_setpvn(obj, (const char*) $1->data, $1->length); printf("get binary_data '%s' [%d] (0x%X)\n", $1->data, $1->length, obj); } else { sv_setsv(obj, &PL_sv_undef); printf("get binary_data [set to undef]\n"); } if( !SvPOK(obj) ) croak("The result is not a scalar string"); $result = obj; } 

I create my Perl module through "ExtUtils :: MakeMaker", and all this is good.

Then I run the following perl script test to ensure that the set / get binary data from the perl string is correct.

 my $fr = ObjectThatContainsBinaryData->new(); my $data = "1234567890"; print ">>>PERL:swig_data_set\n"; $fr->swig_data_set($data); print "<<<PERL:swig_data_set\n"; print ">>>PERL:swig_data_get\n"; my $rdata = $fr->swig_data_get(); print "<<<PERL: swig_data_get\n"; print "sent :" . \$data . " len=" . length($data). " '$data'\n" ."recieved:". \$rdata. " len=" . length($rdata). " '$rdata'\n"; 

Now combined C ++ and Perl printf stdout:

 >>>PERL:swig_data_set set binary_data '1234567890' [10] (0x12B204D0) <<<PERL:swig_data_set >>>PERL:swig_data_get get binary_data '1234567890' [10] (0x1298E4E0) <<<PERL: swig_data_get sent :SCALAR(0x12b204d0) len=10 '1234567890' recieved:SCALAR(0x12bc71c0) len=0 '' 

So why does it look like the perl sv_setpvn call is not working or not working? I don’t know why, when I print the returned binary data in perl, it appears as an empty scalar, but it looks great in the built-in standard SWIG C ++ map.

I use:

Perl v5.8.8 for x86_64-linux-thread-multi

SWIG 2.0.1

gcc version 4.1.1 20070105 (Red Hat 4.1.1-52)

+4
source share
4 answers

If you replace the following line in your% typemap (out):

 $result = obj; 

WITH

 $result = obj; argvi++; //This is a hack to get the hidden stack pointer to increment before the return 

The generated SWIG code will now look like this:

 ... ST(argvi) = obj; argvi++; } XSRETURN(argvi); } 

And your test script will return a Perl string as expected.

 SV = PV(0x1eae7d40) at 0x1eac64d0 REFCNT = 1 FLAGS = (PADBUSY,PADMY,POK,pPOK) PV = 0x1eb25870 "1234567890"\0 CUR = 10 LEN = 16 <<<PERL: swig_data_get sent :SCALAR(0x1ea64530) len=10 '1234567890' recieved:SCALAR(0x1eac64d0) len=10 '1234567890' 

You should read the SWIG 2.0 documentation on types in Perl more carefully:

"30.8.2 Return Values

The return values ​​are pushed onto the argument stack of each wrapper function. The current value of the argument stack pointer is contained in the argvi variable. Whenever a new output value is added, it is important that this value is increased. For multiple output values, the final argvi value should be the total number of output values. "

+2
source

What if you don't make him mortal? I tested using Inline :: C (since I never used SWIG) and installed SV for deadly problems caused since Inline :: C did it for me. Perhaps SWIG uses a similar design?

AND

 SV* obj = newSV(0); sv_setpvn(obj, "abc", 3); 

and

 SV* obj = newSVpvn("abc", 3); 

worked with Inline :: C.

+1
source

swig provides a module called cdata.i . You must include this in the interface definition file.

After enabling this parameter, it gives two functions cdata() and memmove() . Given void * and the length of the binary data, cdata() converts it to the string type of the target language.

memmove() is the opposite. Given a string type, it will copy the contents of the string (including embedded null bytes) into type C void *.

Processing binary data with this module becomes very simple.

Hope this is what you need.

+1
source

On the Perl side you can add

 use Devel::Peek; Dump($fr->swig_data_get()); 

and provide the result? Thanks.

0
source

Source: https://habr.com/ru/post/1343073/


All Articles