5

It's no big deal to get data into Perl 6 Native pointers:

sub memcpy( Pointer[void] $source, Pointer[void] $destination, int32 $size ) is native { * };
my Blob $blob = Blob.new(0x22, 0x33);
my Pointer[void] $src-memcpy = nativecast(Pointer[void], $blob);
my Pointer[void] $dest-memcpy = malloc( 32 );
memcpy($src-memcpy,$dest-memcpy,2);
my Pointer[int] $inter = nativecast(Pointer[int], $dest-memcpy);
say $inter; # prints NativeCall::Types::Pointer[int]<0x4499560>

However, I see no way of getting them out of the Pointer[int] other than creating a function to do it, since nativecast apparently works in the opposite direction, or at least not in the direction of casting to non-native types (which should be obvious by its name). How would you do that?

Update: For instance, using an Array would make it more workable. However

my $inter = nativecast(CArray[int16], $dest);
.say for $inter.list;

This works, but yields the error: Don't know how many elements a C array returned from a library

Update 2: Following Christoph's answer (thanks!), we can ellaborate it a little bit more into this, and we get to put the values back into a Buf

sub malloc(size_t $size --> Pointer) is native {*}
sub memcpy(Pointer $dest, Pointer $src, size_t $size --> Pointer) is native {*}

my $blob = Blob.new(0x22, 0x33);
my $src = nativecast(Pointer, $blob);
my $dest = malloc( $blob.bytes );
memcpy($dest, $src, $blob.bytes);
my $inter = nativecast(Pointer[int8], $dest);

my $cursor = $inter;

my Buf $new-blob .= new() ;
for 1..$blob.bytes {
    $new-blob.append: $cursor.deref;
    $cursor++;
}

say $new-blob;

We need to cast the pointer to exactly the same type used by the buffer, and then we use pointer arithmetic to run over it. However, we use $blob.bytes to know when to end the loop, and it's still kind of hacky. Would there be a more direct way? Or just a way of working with Bufs/Blobs so that they can be copied easily to the Native realm and back?

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
jjmerelo
  • 22,578
  • 8
  • 40
  • 86

2 Answers2

7

Complete example:

use NativeCall;

sub malloc(size_t $size --> Pointer) is native {*}
sub memcpy(Pointer $dest, Pointer $src, size_t $size --> Pointer) is native {*}

my $blob = Blob.new(0x22, 0x33);
my $src = nativecast(Pointer, $blob);
my $dest = malloc(nativesizeof(int16));
memcpy($dest, $src, nativesizeof(int16));
my $inter = nativecast(Pointer[int16], $dest);
say $inter.deref.fmt('%x');

I assume you were looking for the deref method, but there were a few other issues with your code:

  • use of int32 instead of size_t in the declaration of memcpy
  • wrong order of the arguments to memcpy, which means you were copying two bytes read from uninitialized memory into your supposedly immutable Blob
  • use of plain int where you probably should have used a sized integer type such as int16

Also note the use of plain Pointer in the declaration of memcpy, which will allow you to pass in any pointer type without having to cast constantly.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • Well, kinda, but `$inter` is still a `Pointer[int16]`, a native pointer, even if I can print it (for some reason, in reverse order) – jjmerelo Jun 28 '18 at 15:27
  • Also, wouldn't that be `$blob.bytes` instead of `nativesizeof(int16)`? Thanks a lot for your answer, anyway. – jjmerelo Jun 28 '18 at 15:34
  • 2
    @jjmerelo: the reverse order is due to [little endian](https://en.wikipedia.org/wiki/Endianness#Little-endian) integer encoding; re your 2nd comment, I chose `nativesizeof(int16)` because it's arguably 'safer' to provide the size of the destination argument when possible; note that I assumed your goal was creating a 16-bit integer, not an arbitrary native blob – Christoph Jun 28 '18 at 18:30
2

Use NativeHelpers::Blob:

use NativeCall;
use NativeHelpers::Blob;

sub malloc(size_t $size --> Pointer) is native {*}
sub memcpy(Pointer $dest, Pointer $src, size_t $size --> Pointer) is native {*}

my $blob = Blob.new(0x22, 0x33);
my $src = nativecast(Pointer, $blob);
my $dest = malloc( $blob.bytes );
memcpy($dest, $src, $blob.bytes);
my $inter = nativecast(Pointer[int8], $dest);

my Blob $esponja = blob-from-pointer( $inter, :2elems, :type(Blob[int8]));
say $esponja;

This program extends Christoph's answer adding the blob-from-pointer function, which will return the original blob: Blob[int8]:0x<22 33>. This answer is also now in the documentation

jjmerelo
  • 22,578
  • 8
  • 40
  • 86