Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,283 @@
package FFI::Platypus::API;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use base qw( Exporter );
our @EXPORT = grep /^arguments_/, keys %FFI::Platypus::API::;
# ABSTRACT: Platypus arguments and return value API for custom types
our $VERSION = '1.34'; # VERSION
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::API - Platypus arguments and return value API for custom types
=head1 VERSION
version 1.34
=head1 SYNOPSIS
package FFI::Platypus::Type::MyCustomType;
use FFI::Platypus::API;
sub ffi_custom_type_api_1
{
{
native_type => 'uint32',
perl_to_native => sub {
my($value, $i) = @_;
# Translates ($value) passed in from Perl
# into ($value+1, $value+2)
arguments_set_uint32($i, $value+1);
arguments_set_uint32($i+1, $value+2);
},
argument_count => 2,
}
}
=head1 DESCRIPTION
B<NOTE>: I added this interface early on to L<FFI::Platypus>, but haven't
used it much, generally finding function wrappers to be a more powerful
(although possibly not as fast) interface. It has thus not been
tested as much as the rest of Platypus. If you feel the need to use
this interface please coordinate with the Platypus developers.
The custom types API for L<FFI::Platypus> allows you to set multiple C
arguments from a single Perl argument as a common type. This is
sometimes useful for pointer / size pairs which are a common pattern in
C, but are usually represented by a single value (a string scalar) in
Perl.
The custom type API is somewhat experimental, and you should expect some
changes as needs arise (I won't break compatibility lightly, however).
=head1 FUNCTIONS
These functions are only valid within a custom type callback.
=head2 arguments_count
my $count = argument_count;
Returns the total number of native arguments.
=head2 arguments_get_sint8
my $sint8 = arguments_get_sint8 $i;
Get the 8 bit signed integer argument from position I<$i>.
=head2 arguments_set_sint8
arguments_set_sint8 $i, $sint8;
Set the 8 bit signed integer argument at position I<$i> to I<$sint8>.
=head2 arguments_get_uint8
my $uint8 = arguments_get_uint8 $i;
Get the 8 bit unsigned integer argument from position I<$i>.
=head2 arguments_set_uint8
arguments_set_uint8 $i, $uint8;
Set the 8 bit unsigned integer argument at position I<$i> to I<$uint8>.
=head2 arguments_get_sint16
my $sint16 = arguments_get_sint16 $i;
Get the 16 bit signed integer argument from position I<$i>.
=head2 arguments_set_sint16
arguments_set_sint16 $i, $sint16;
Set the 16 bit signed integer argument at position I<$i> to I<$sint16>.
=head2 arguments_get_uint16
my $uint16 = arguments_get_uint16 $i;
Get the 16 bit unsigned integer argument from position I<$i>.
=head2 arguments_set_uint16
arguments_set_uint16 $i, $uint16;
Set the 16 bit unsigned integer argument at position I<$i> to I<$uint16>.
=head2 arguments_get_sint32
my $sint32 = arguments_get_sint32 $i;
Get the 32 bit signed integer argument from position I<$i>.
=head2 arguments_set_sint32
arguments_set_sint32 $i, $sint32;
Set the 32 bit signed integer argument at position I<$i> to I<$sint32>.
=head2 arguments_get_uint32
my $uint32 = arguments_get_uint32 $i;
Get the 32 bit unsigned integer argument from position I<$i>.
=head2 arguments_set_uint32
arguments_set_uint32 $i, $uint32;
Set the 32 bit unsigned integer argument at position I<$i> to I<$uint32>.
=head2 arguments_get_sint64
my $sint64 = arguments_get_sint64 $i;
Get the 64 bit signed integer argument from position I<$i>.
=head2 arguments_set_sint64
arguments_set_sint64 $i, $sint64;
Set the 64 bit signed integer argument at position I<$i> to I<$sint64>.
=head2 arguments_get_uint64
my $uint64 = arguments_get_uint64 $i;
Get the 64 bit unsigned integer argument from position I<$i>.
=head2 arguments_set_uint64
arguments_set_uint64 $i, $uint64;
Set the 64 bit unsigned integer argument at position I<$i> to I<$uint64>.
=head2 arguments_get_float
my $float = arguments_get_float $i;
Get the floating point argument from position I<$i>.
=head2 arguments_set_float
arguments_set_float $i, $float;
Set the floating point argument at position I<$i> to I<$float>
=head2 arguments_get_double
my $double = arguments_get_double $i;
Get the double precision floating point argument from position I<$i>.
=head2 arguments_set_double
arguments_set_double $i, $double;
Set the double precision floating point argument at position I<$i> to
I<$double>
=head2 arguments_get_pointer
my $pointer = arguments_get_pointer $i;
Get the pointer argument from position I<$i>.
=head2 arguments_set_pointer
arguments_set_pointer $i, $pointer;
Set the pointer argument at position I<$i> to I<$pointer>.
=head2 arguments_get_string
my $string = arguments_get_string $i;
Get the string argument from position I<$i>.
=head2 arguments_set_string
arguments_set_string $i, $string;
Set the string argument at position I<$i> to I<$string>.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
=back
Examples of use:
=over 4
=item L<FFI::Platypus::Type::PointerSizeBuffer>
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,356 @@
package FFI::Platypus::Buffer;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use base qw( Exporter );
our @EXPORT = qw( scalar_to_buffer buffer_to_scalar );
our @EXPORT_OK = qw ( scalar_to_pointer grow set_used_length window );
# ABSTRACT: Convert scalars to C buffers
our $VERSION = '1.34'; # VERSION
use constant _incantation =>
$^O eq 'MSWin32' && do { require Config; $Config::Config{archname} =~ /MSWin32-x64/ }
? 'Q'
: 'L!';
sub scalar_to_buffer ($)
{
(unpack(_incantation, pack 'P', $_[0]), do { use bytes; length $_[0] });
}
sub scalar_to_pointer ($)
{
unpack(_incantation, pack 'P', $_[0]);
}
sub buffer_to_scalar ($$)
{
unpack 'P'.$_[1], pack _incantation, defined $_[0] ? $_[0] : 0;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Buffer - Convert scalars to C buffers
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus::Buffer;
my($pointer, $size) = scalar_to_buffer $scalar;
my $scalar2 = buffer_to_scalar $pointer, $size;
=head1 DESCRIPTION
A common pattern in C is to pass a "buffer" or region of memory into a
function with a pair of arguments, an opaque pointer and the size of the
memory region. In Perl the equivalent structure is a scalar containing
a string of bytes. This module provides portable functions for
converting a Perl string or scalar into a buffer and back.
These functions are implemented using L<pack and unpack|perlpacktut> and
so they should be relatively fast.
Both functions are exported by default, but you can explicitly export
one or neither if you so choose.
A better way to do this might be with custom types see
L<FFI::Platypus::API> and L<FFI::Platypus::Type>. These functions were
taken from the now obsolete L<FFI::Util> module, as they may be useful
in some cases.
B<Caution>: This module provides great power in the way that you
interact with C code, but with that power comes great responsibility.
Since you are dealing with blocks of memory you need to take care to
understand the underlying ownership model of these pointers.
=head1 FUNCTIONS
=head2 scalar_to_buffer
my($pointer, $size) = scalar_to_buffer $scalar;
Convert a string scalar into a buffer. Returned in order are a pointer
to the start of the string scalar's memory region and the size of the
region.
You should NEVER try to free C<$pointer>.
When you pass this pointer and size into a C function, it has direct
access to the data stored in your scalar, so it is important that you
not resize or free the scalar while it is in use by the C code. Typically
if you are passing a buffer into a C function which reads or writes to
the buffer, but does not keep the pointer for later use you are okay.
If the buffer is in use long term by the C code, then you should consider
copying the buffer instead. For example:
use FFI::Platypus::Buffer qw( scalar_to_buffer );
use FFI::Platypus::Memory qw( malloc memcpy free )
my($ptr, $size) = scalar_to_buffer $string;
c_function_thaat_does_not_keep_ptr( $ptr, $size); # okay
my($ptr, $size) = scalar_to_buffer $string;
my $ptr_copy = malloc($size);
memcpy($ptr_copy, $ptr, $size);
c_function_that_DOES_keep_ptr( $ptr_copy, $size); # also okay
...
# later when you know that the c code is no longer using the pointer
# Since you allocated the copy, you are responsible for free'ing it.
free($ptr_copy);
=head2 scalar_to_pointer
my $pointer = scalar_to_pointer $scalar;
Get the pointer to the scalar. (Similar to C<scalar_to_buffer> above, but
the size of the scalar is not computed or returned).
Not exported by default, but may be exported on request.
=head2 buffer_to_scalar
my $scalar = buffer_to_scalar $pointer, $size;
Convert the buffer region defined by the pointer and size into a string
scalar.
Because of the way memory management works in Perl, the buffer is copied
from the buffer into the scalar. If this pointer was returned from C
land, then you should only free it if you allocated it.
=head2 grow
grow $scalar, $size, \%options;
Ensure that the scalar can contain at least C<$size> bytes. The
following are recognized:
=over
=item clear => I<boolean>
If true, C<$scalar> is cleared prior to being enlarged. This
avoids copying the existing contents to the reallocated memory
if they are not needed.
For example, after
$scalar = "my string";
grow $scalar, 100, { clear => 0 };
C<$scalar == "my string">, while after
$scalar = "my string";
grow $scalar, 100;
C<length($scalar) == 0>
It defaults to C<true>.
=item set_length => I<boolean>
If true, the length of the I<string> in the C<$scalar> is set to C<$size>.
(See the discussion in L</set_used_length>.) This is useful if a
foreign function writes exactly C<$size> bytes to C<$scalar>, as it avoids
a subsequent call to C<set_used_length>. Contrast this
grow my $scalar, 100;
read_exactly_100_bytes_into_scalar( scalar_to_pointer($scalar) );
@chars = unpack( 'c*', $scalar );
with this:
grow my $scalar, 100, { set_length => 0 };
read_exactly_100_bytes_into_scalar( scalar_to_pointer($scalar) );
set_used_length( $scalar, 100 );
@chars = unpack( 'c*', $scalar );
It defaults to C<true>.
=back
Any pointers obtained with C<scalar_to_pointer> or C<scalar_to_buffer>
are no longer valid after growing the scalar.
Not exported by default, but may be exported on request.
=head2 set_used_length
set_used_length $scalar, $length;
Update Perl's notion of the length of the string in the scalar. A
string scalar keeps track of two lengths: the number of available
bytes and the number of used bytes. When a string scalar is
used as a buffer by a foreign function, it is necessary to indicate
to Perl how many bytes were actually written to it so that Perl's
string functions (such as C<substr> or C<unpack>) will work correctly.
If C<$length> is larger than what the scalar can hold, it is set to the
maximum possible size.
In the following example, the foreign routine C<read_doubles>
may fill the buffer with up to a set number of doubles, returning the
number actually written.
my $sizeof_double = $ffi->sizeof( 'double' );
my $max_doubles = 100;
my $max_length = $max_doubles * $sizeof_double;
my $buffer; # length($buffer) == 0
grow $buffer, $max_length; # length($buffer) is still 0
my $pointer = scalar_to_pointer($buffer);
my $num_read = read_doubles( $pointer, $max_doubles );
# length($buffer) is still == 0
set_used_length $buffer, $num_read * $sizeof_double;
# length($buffer) is finally != 0
# unpack the native doubles into a Perl array
my @doubles = unpack( 'd*', $buffer ); # @doubles == $num_read
Not exported by default, but may be exported on request.
=head2 window
window $scalar, $pointer;
window $scalar, $pointer, $size;
window $scalar, $pointer, $size, $utf8;
This makes the scalar a read-only window into the arbitrary region of
memory defined by C<$pointer>, pointing to the start of the region
and C<$size>, the size of the region. If C<$size> is omitted then
it will assume a C style string and use the C C<strlen> function to
determine the size (the terminating C<'\0'> will not be included).
This can be useful if you have a C function that returns a buffer
pair (pointer, size), and want to access it from Perl without having
to copy the data. This can also be useful when interfacing with
programming languages that store strings as a address/length pair
instead of a pointer to null-terminated sequence of bytes.
You can specify C<$utf8> to set the UTF-8 flag on the scalar. Note
that the behavior of setting the UTF-8 flag on a buffer that does
not contain UTF-8 as understood by the version of Perl that you are
running is undefined.
I<Hint>: If you have a buffer that needs to be free'd by C once the
scalar falls out of scope you can use L<Variable::Magic> to apply
magic to the scalar and free the pointer once it falls out of scope.
use FFI::Platypus::Buffer qw( scalar_to_pointer );
use FFI::Platypus::Memory qw( strdup free );
use Variable::Magic qw( wizard cast );
my $free_when_out_of_scope = wizard(
free => sub {
my $ptr = scalar_to_pointer ${$_[0]};
free $ptr;
}
);
my $ptr = strdup "Hello Perl";
my $scalar;
window $scalar, $ptr, 10;
cast $scalar, $free_when_out_of_scope;
undef $ptr; # don't need to track the pointer anymore.
# we can now use scalar as a regular read-only Perl variable
print $scalar, "\n"; # prints "Hello Perl" without the \0
# this will free the C pointer
undef $scalar;
I<Hint>: Returning a scalar string from a Perl function actually
copies the value. If you want to return a string without copying
then you need to return a reference.
sub c_string
{
my $ptr = strdup "Hello Perl";
my $scalar;
window $scalar, $ptr, 10;
cast $scalar, $free_when_out_of_scope;
\$scalar;
}
my $ref = c_string();
print $$ref, "\n"; # prints "Hello Perl" without the \0
Not exported by default, but may be exported on request.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
Main Platypus documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,625 @@
package FFI::Platypus::Bundle;
use strict;
use warnings;
use 5.008004;
use Carp ();
# ABSTRACT: Bundle foreign code with your Perl module
our $VERSION = '1.34'; # VERSION
package FFI::Platypus;
sub _bundle
{
my @arg_ptrs;
if(defined $_[-1] && ref($_[-1]) eq 'ARRAY')
{
@arg_ptrs = @{ pop @_ };
}
push @arg_ptrs, undef;
my($self, $package) = @_;
$package = caller unless defined $package;
require List::Util;
my($pm) = do {
my $pm = "$package.pm";
$pm =~ s{::}{/}g;
# if the module is already loaded, we can use %INC
# otherwise we can go through @INC and find the first .pm
# this doesn't handle all edge cases, but probably enough
List::Util::first(sub { (defined $_) && (-f $_) }, ($INC{$pm}, map { "$_/$pm" } @INC));
};
Carp::croak "unable to find module $package" unless $pm;
my @parts = split /::/, $package;
my $incroot = $pm;
{
my $c = @parts;
$incroot =~ s![\\/][^\\/]+$!! while $c--;
}
my $txtfn = List::Util::first(sub { -f $_ }, do {
my $dir = join '/', @parts;
my $file = $parts[-1] . ".txt";
(
"$incroot/auto/$dir/$file",
"$incroot/../arch/auto/$dir/$file",
);
});
my $lib;
if($txtfn)
{
$lib = do {
my $fh;
open($fh, '<', $txtfn) or die "unable to read $txtfn $!";
my $line = <$fh>;
close $fh;
$line =~ /^FFI::Build\@(.*)$/
? "$incroot/$1"
: Carp::croak "bad format $txtfn";
};
Carp::croak "bundle code is missing: $lib" unless -f $lib;
}
elsif(-d "$incroot/../ffi")
{
require FFI::Build::MM;
require Capture::Tiny;
require Cwd;
require File::Spec;
my $save = Cwd::getcwd();
chdir "$incroot/..";
my($output, $error) = Capture::Tiny::capture_merged(sub {
$lib = eval {
my $dist_name = $package;
$dist_name =~ s/::/-/g;
my $fbmm = FFI::Build::MM->new( save => 0 );
$fbmm->mm_args( DISTNAME => $dist_name );
my $build = $fbmm->load_build('ffi', undef, 'ffi/_build');
$build->build;
};
$@;
});
if($error)
{
chdir $save;
print STDERR $output;
die $error;
}
else
{
$lib = File::Spec->rel2abs($lib);
chdir $save;
}
}
else
{
Carp::croak "unable to find bundle code for $package";
}
my $handle = FFI::Platypus::DL::dlopen($lib, FFI::Platypus::DL::RTLD_PLATYPUS_DEFAULT())
or Carp::croak "error loading bundle code: $lib @{[ FFI::Platypus::DL::dlerror() ]}";
$self->{handles}->{$lib} = $handle;
$self->lib($lib);
if(my $init = eval { $self->function( 'ffi_pl_bundle_init' => [ 'string', 'sint32', 'opaque[]' ] => 'void' ) })
{
$init->call($package, scalar(@arg_ptrs)-1, \@arg_ptrs);
}
if(my $init = eval { $self->function( 'ffi_pl_bundle_constant' => [ 'string', 'opaque' ] => 'void' ) })
{
require FFI::Platypus::Constant;
my $api = FFI::Platypus::Constant->new($package);
$init->call($package, $api->ptr);
}
if(my $address = $self->find_symbol( 'ffi_pl_bundle_fini' ))
{
push @{ $self->{fini} }, sub {
my $self = shift;
$self->function( $address => [ 'string' ] => 'void' )
->call( $package );
};
}
$self;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Bundle - Bundle foreign code with your Perl module
=head1 VERSION
version 1.34
=head1 SYNOPSIS
C<ffi/foo.c>:
#include <ffi_platypus_bundle.h>
#include <string.h>
typedef struct {
char *name;
int value;
} foo_t;
foo_t*
foo__new(const char *class_name, const char *name, int value)
{
(void)class_name;
foo_t *self = malloc( sizeof( foo_t ) );
self->name = strdup(name);
self->value = value;
return self;
}
const char *
foo__name(foo_t *self)
{
return self->name;
}
int
foo__value(foo_t *self)
{
return self->value;
}
void
foo__DESTROY(foo_t *self)
{
free(self->name);
free(self);
}
C<lib/Foo.pm>:
package Foo;
use strict;
use warnings;
use FFI::Platypus;
{
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->type('object(Foo)' => 'foo_t');
$ffi->mangler(sub {
my $name = shift;
$name =~ s/^/foo__/;
$name;
});
$ffi->bundle;
$ffi->attach( new => [ 'string', 'string', 'int' ] => 'foo_t' );
$ffi->attach( name => [ 'foo_t' ] => 'string' );
$ffi->attach( value => [ 'foo_t' ] => 'int' );
$ffi->attach( DESTROY => [ 'foo_t' ] => 'void' );
}
1;
C<t/foo.t>
use Test::More;
use Foo;
my $foo = Foo->new("platypus", 10);
isa_ok $foo, 'Foo';
is $foo->name, "platypus";
is $foo->value, 10;
done_testing;
C<Makefile.PL>:
use ExtUtils::MakeMaker;
use FFI::Build::MM;
my $fbmm = FFI::Build::MM->new;
WriteMakefile(
$fbmm->mm_args(
NAME => 'Foo',
DISTNAME => 'Foo',
VERSION => '1.00',
# ...
)
);
sub MY::postamble
{
$fbmm->mm_postamble;
}
or C<dist.ini>:
name = Foo
version = 0.01
...
[FFI::Build]
version = 1.04
=head1 DESCRIPTION
This document serves as a tutorial for using the new bundling interface provided
by L<FFI::Platypus> as of api version 1. It requires L<FFI::Platypus> of at least
1.00.
Sometimes when writing FFI bindings you need to include a little C code (or your
favorite compiled language) to finish things off. Alternatively, you might just
want to write some C code (or your favorite compiled language) to include with your
Perl module to make a tight loop faster. The bundling interface has you covered.
=head2 Basic example
To illustrate we will go through the files in the synopsis and explain
how and why they work. To start with we have some C code which emulates object
oriented code using C<foo__> as a prefix. We use a C struct that we call
C<foo_t> to store our object data. On the C level the struct acts as a class,
when combined with its functions that act as methods. The constructor just
allocates the memory it needs for the C<foo_t> instance, fills in the
appropriate fields and returns the pointer:
foo_t*
foo__new(const char *class_name, const char *name, int value)
{
(void) class_name;
foo_t *self = malloc( sizeof( foo_t ) );
self->name = strdup(name);
self->value = value;
return self;
}
We include a class name as the first argument, because Perl will include that
when calling the constructor, but we do not use it here. An exercise for the
reader would be to add hierarchical inheritance.
There are also some methods which return member values. This class has only
read only members, but you could have read/write or other methods depending
on your needs.
const char *
foo__name(foo_t *self)
{
return self->name;
}
We also include a destructor so that the memory owned by the object can be
freed when it is no longer needed.
void
foo__DESTROY(foo_t *self)
{
free(self->name);
free(self);
}
This might start to look a little like a Perl module, and when we look at the Perl
code that binds to this code, you will see why. First lets prepare the
L<FFI::Platypus> instance and specify the correct api version:
my $ffi = FFI::Platypus->new( api => 1 );
The bundle interface is only supported with api version 1, so if you try to use
version 0 it will not work. Next we define an object type for C<foo_t> which will
associate it with the Perl class C<Foo>.
$ffi->type('object(Foo)' => 'foo_t');
As object type is a blessed reference to an opaque (default) or integer type which
can be used as a Perl object. Platypus does the translating of Perl object to and
from the foo_t pointers that the C code understands. For more details on Platypus
types see L<FFI::Platypus::Type>.
Next we set the mangler on the Platypus instance so that we can refer to function
names without the C<foo__> prefix. You could just not use the prefix in your C
code and skip this step, or you could refer to the function names in their full
in your Perl code, however, this saves extra typing and allows you to bundle more
than one class with your Perl code without having to worry about name conflicts.
$ffi->mangler(sub {
my $name = shift;
$name =~ s/^/foo__/;
$name;
});
Finally we let Platypus know that we will be bundling code.
$ffi->bundle;
By default, this searches for the appropriate place for your dynamic libraries using
the current package. In some cases you may need to override this, for example if your
dist is named C<Foo-Bar> but your specific class is named C<Foo::Bar::Baz>, you'd
want something like this:
package Foo::Bar::Baz;
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->bundle('Foo::Bar');
...
Now, finally we can attach the methods for our class:
$ffi->attach( new => [ 'string', 'int' ] => 'foo_t' );
$ffi->attach( name => [ 'foo_t' ] => 'string' );
$ffi->attach( value => [ 'foo_t' ] => 'int' );
$ffi->attach( DESTROY => [ 'foo_t' ] => 'void' );
Note that we do not have to include the C<foo__> prefix because of the way we set up
the mangler. If we hadn't done that then we could instead attach with the full names:
$ffi->attach( [ 'foo__new' => 'new' ] => [ 'string', 'int' ] => 'foo_t' );
$ffi->attach( [ 'foo__name' => 'name' ] => [ 'foo_t' ] => 'string' );
...
You're done! You can now use this class. Lets write a test to make sure it works,
use strict;
use warnings;
use Test::More;
use Foo;
my $foo = Foo->new("platypus", 10);
isa_ok $foo, 'Foo';
is $foo->name, "platypus";
is $foo->value, 10;
done_testing;
and use C<prove> to check that it works:
% prove -lvm
t/foo.t ..
ok 1 - An object of class 'Foo' isa 'Foo'
ok 2
ok 3
1..3
ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.14 cusr 0.03 csys = 0.19 CPU)
Result: PASS
Platypus automatically compiles and links the dynamic library for you:
% ls ffi/_build
foo.c.o libFoo.so
The C code will be rebuilt next time if the source code is newer than the object or dynamic libraries
files. If the source files are not changed, then it won't be rebuilt to save time. If you are using
the code without MakeMaker, or another build system you are responsible for cleaning up these files.
This is intended as a convenience to allow you to test your code without having to invoke MakeMaker,
or C<dzil> or whatever build system you are using.
When you distribute your module though, you will want the dynamic library built just once
at build-time and installed correctly so that it can be found at run-time. You don't need
to make any changes to your C or Perl code, but you do need to tell MakeMaker to build and
install the appropriate files using L<FFI::Build::MM>:
use ExtUtils::MakeMaker;
use FFI::Build::MM;
my $fbmm = FFI::Build::MM->new;
WriteMakefile(
$fbmm->mm_args(
NAME => 'Foo',
DISTNAME => 'Foo',
VERSION => '1.00',
# ...
)
);
sub MY::postamble
{
$fbmm->mm_postamble;
}
And we can invoke all the normal MakeMaker style stuff and our C code will be compiled, linked
and installed at the appropriate steps.
% perl Makefile.PL
Generating a Unix-style Makefile
Writing Makefile for Foo
Writing MYMETA.yml and MYMETA.json
% make
cp lib/Foo.pm blib/lib/Foo.pm
"/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
CC ffi/foo.c
LD blib/lib/auto/share/dist/Foo/lib/libFoo.dylib
% make test
"/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_build
"/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" -MFFI::Build::MM=cmd -e fbx_test
PERL_DL_NONLAZY=1 "/Users/ollisg/perl5/perlbrew/perls/perl-5.30.0/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/foo.t .. ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.06 cusr 0.01 csys = 0.08 CPU)
Result: PASS
If the C<Makefile.PL> file above looks overly complicated, you can use the
L<Dist::Zilla::Plugin::FFI::Build> plugin to simplify your life if you are
using L<Dist::Zilla>:
[FFI::Build]
version = 1.04
Specifying version 1.04 will ensure that any C<.o> or C<.so> files are pruned
from your build tree and not distributed by mistake.
=head2 Initialization example
The bundle interface also gives you entry points which will be called automatically
when your code is loaded and unloaded if they are found.
=over 4
=item C<ffi_pl_bundle_init>
void ffi_pl_bundle_init(const char *package, int argc, void *argv[]);
Called when the dynamic library is loaded. C<package> is the Perl package
that called C<bundle> from Perl space. C<argc> and C<argv> represents an
array of opaque pointers that can be passed as an array to bundle as the
last argument. (the count C<argc> is a little redundant because C<argv>
is also NULL terminated).
=item C<ffi_pl_bundle_constant>
void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c);
Called immediately after C<ffi_pl_bundle_init>, and is intended to allow
you to set Perl constants from C space. For details on how this works
and what methods you can call on the C<ffi_platypus_constant_t> instance,
see L<FFI::Platypus::Constant>.
=item C<ffi_pl_bundle_fini>
void ffi_pl_bundle_fini(const char *package);
Called when the dynamic library is unloaded. C<package> is the Perl
package that called C<bundle> from Perl space when the library was
loaded. B<CAVEAT>: if you attach any functions then this will
never be called, because attaching functions locks the Platypus
instance into memory along with the libraries which it is using.
=back
Here is an example that passes the version and a callback back into Perl
space that emulates the Perl 5.10 C<say> feature.
C<ffi/init.c>:
#include <ffi_platypus_bundle.h>
char buffer[512];
const char *version;
void (*say)(const char *);
void
ffi_pl_bundle_init(const char *package, int argc, void *argv[])
{
version = argv[0];
say = argv[1];
say("in init!");
snprintf(buffer, 512, "package = %s, version = %s", package, version);
say(buffer);
snprintf(buffer, 512, "args = %d", argc);
say(buffer);
}
void
ffi_pl_bundle_fini(const char *package)
{
say("in fini!");
}
C<lib/Init.pm>:
package Init;
use strict;
use warnings;
use FFI::Platypus;
our $VERSION = '1.00';
{
my $ffi = FFI::Platypus->new( api => 1 );
my $say = $ffi->closure(sub {
my $string = shift;
print "$string\n";
});
$ffi->bundle([
$ffi->cast( 'string' => 'opaque', $VERSION ),
$ffi->cast( '(string)->void' => 'opaque', $say ),
]);
undef $ffi;
undef $say;
}
1;
The deinitialization order for the C<$say> callback and the C<$ffi>
instance is essential here, so we do it manually with C<undef>:
undef $ffi;
undef $say;
First we deallocate C<$ffi> which calls C<ffi_pl_bundle_fini>,
which calls C<$say>, so we want to make sure the latter is still
allocated. Once C<ffi_pl_bundle_fini> is done, we can safely
deallocate C<$say>.
If C<ffi_pl_bundle_fini> didn't call back into Perl space like
this then we don't have to be as careful about deallocating
things in Perl space.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,189 @@
package FFI::Platypus::Closure;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use Scalar::Util qw( refaddr);
use Carp qw( croak );
use overload '&{}' => sub {
my $self = shift;
sub { $self->{code}->(@_) };
}, bool => sub { 1 }, fallback => 1;
# ABSTRACT: Platypus closure object
our $VERSION = '1.34'; # VERSION
sub new
{
my($class, $coderef) = @_;
croak "not a coderef" unless ref($coderef) eq 'CODE';
my $self = bless { code => $coderef, cbdata => {}, sticky => 0 }, $class;
$self;
}
sub add_data
{
my($self, $payload, $type) = @_;
$self->{cbdata}{$type} = bless \$payload, 'FFI::Platypus::ClosureData';
}
sub get_data
{
my($self, $type) = @_;
if (exists $self->{cbdata}->{$type}) {
return ${$self->{cbdata}->{$type}};
}
return 0;
}
sub call
{
my $self = shift;
$self->{code}->(@_)
}
sub sticky
{
my($self) = @_;
return if $self->{sticky};
$self->{sticky} = 1;
$self->_sticky;
}
sub unstick
{
my($self) = @_;
return unless $self->{sticky};
$self->{sticky} = 0;
$self->_unstick;
}
package FFI::Platypus::ClosureData;
our $VERSION = '1.34'; # VERSION
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Closure - Platypus closure object
=head1 VERSION
version 1.34
=head1 SYNOPSIS
create closure with OO interface
use FFI::Platypus::Closure;
my $closure = FFI::Platypus::Closure->new(sub { print "hello world\n" });
create closure from Platypus object
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
my $closure = $ffi->closure(sub { print "hello world\n" });
use closure
$ffi->function(foo => ['()->void'] => 'void')->call($closure);
=head1 DESCRIPTION
This class represents a Perl code reference that can be called from compiled code.
When you create a closure object, you can pass it into any function that expects
a function pointer. Care needs to be taken with closures because compiled languages
typically have a different way of handling lifetimes of objects. You have to make
sure that if the compiled code is going to call a closure that the closure object
is still in scope somewhere, or has been made sticky, otherwise you may get a
segment violation or other mysterious crash.
=head1 CONSTRUCTOR
=head2 new
my $closure = FFI::Platypus::Closure->new($coderef);
Create a new closure object; C<$coderef> must be a subroutine code reference.
=head1 METHODS
=head2 call
$closure->call(@arguments);
$closure->(@arguments);
Call the closure from Perl space. May also be invoked by treating
the closure object as a code reference.
=head2 sticky
$closure->sticky;
Mark the closure sticky, meaning that it won't be free'd even if
all the reference of the object fall out of scope.
=head2 unstick
$closure->unstick;
Unmark the closure as sticky.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,236 @@
package FFI::Platypus::Constant;
use strict;
use warnings;
use 5.008004;
use constant 1.32 ();
use FFI::Platypus;
# ABSTRACT: Define constants in C space for Perl
our $VERSION = '1.34'; # VERSION
{
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->bundle;
$ffi->type( 'opaque' => 'ffi_platypus_constant_t' );
$ffi->type( '(string,string)->void' => 'set_str_t' );
$ffi->type( '(string,sint64)->void' => 'set_sint_t' );
$ffi->type( '(string,uint64)->void' => 'set_uint_t' );
$ffi->type( '(string,double)->void' => 'set_double_t' );
$ffi->mangler(sub {
my($name) = @_;
$name =~ s/^/ffi_platypus_constant__/;
$name;
});
$ffi->attach( new => [ 'set_str_t', 'set_sint_t', 'set_uint_t', 'set_double_t' ] => 'ffi_platypus_constant_t' => sub {
my($xsub, $class, $default_package) = @_;
my $f = $ffi->closure(sub {
my($name, $value) = @_;
if($name !~ /::/)
{
$name = join('::', $default_package, $name);
}
constant->import($name, $value);
});
bless {
ptr => $xsub->($f, $f, $f, $f),
f => $f,
}, $class;
});
$ffi->attach( DESTROY => ['ffi_platypus_constant_t'] => 'void' => sub {
my($xsub, $self) = @_;
$xsub->($self->ptr);
});
sub ptr { shift->{ptr} }
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Constant - Define constants in C space for Perl
=head1 VERSION
version 1.34
=head1 SYNOPSIS
C<ffi/foo.c>:
#include <ffi_platypus_bundle.h>
void
ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c)
{
c->set_str("FOO", "BAR"); /* sets $package::FOO to "BAR" */
c->set_str("ABC::DEF", "GHI"); /* sets ABC::DEF to GHI */
}
C<lib/Foo.pm>:
package Foo;
use strict;
use warnings;
use FFI::Platypus;
use base qw( Exporter );
my $ffi = FFI::Platypus->new;
# sets constatns Foo::FOO and ABC::DEF from C
$ffi->bundle;
1;
=head1 DESCRIPTION
The Platypus bundle interface (see L<FFI::Platypus::Bundle>) has an entry point
C<ffi_pl_bundle_constant> that lets you define constants in Perl space from C.
void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c);
The first argument C<package> is the name of the Perl package. The second argument
C<c> is a struct with function pointers that lets you define constants of different
types. The first argument for each function is the name of the constant and the
second is the value. If C<::> is included in the constant name then it will be
defined in that package space. If it isn't then the constant will be defined in
whichever package called C<bundle>.
=over 4
=item set_str
c->set_str(name, value);
Sets a string constant.
=item set_sint
c->set_sint(name, value);
Sets a 64-bit signed integer constant.
=item set_uint
c->set_uint(name, value);
Sets a 64-bit unsigned integer constant.
=item set_double
c->set_double(name, value);
Sets a double precision floating point constant.
=back
=head2 Example
Suppose you have a header file C<myheader.h>:
#ifndef MYHEADER_H
#define MYHEADER_H
#define MYVERSION_STRING "1.2.3"
#define MYVERSION_MAJOR 1
#define MYVERSION_MINOR 2
#define MYVERSION_PATCH 3
enum {
MYBAD = -1,
MYOK = 1
};
#define MYPI 3.14
#endif
You can define these constants from C:
#include <ffi_platypus_bundle.h>
#include "myheader.h"
void ffi_pl_bundle_constant(const char *package, ffi_platypus_constant_t *c)
{
c->set_str("MYVERSION_STRING", MYVERSION_STRING);
c->set_uint("MYVERSION_MAJOR", MYVERSION_MAJOR);
c->set_uint("MYVERSION_MINOR", MYVERSION_MINOR);
c->set_uint("MYVERSION_PATCH", MYVERSION_PATCH);
c->set_sint("MYBAD", MYBAD);
c->set_sint("MYOK", MYOK);
c->set_double("MYPI", MYPI);
}
Your Perl code doesn't have to do anything when calling bundle:
package Const;
use strict;
use warnings;
use FFI::Platypus;
{
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->bundle;
}
1;
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,221 @@
package FFI::Platypus::DL;
use strict;
use warnings;
use 5.008004;
use base qw( Exporter );
require FFI::Platypus;
our @EXPORT = qw( dlopen dlerror dlsym dlclose );
push @EXPORT, grep /RTLD_/, keys %FFI::Platypus::DL::;
# ABSTRACT: Slightly non-portable interface to libdl
our $VERSION = '1.34'; # VERSION
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::DL - Slightly non-portable interface to libdl
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
use FFI::Platypus::DL;
my $handle = dlopen("./libfoo.so", RTLD_PLATYPUS_DEFAULT);
my $address = dlsym($handle, "my_function_named_foo");
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->function($address => [] => 'void')->call;
dlclose($handle);
=head1 DESCRIPTION
This module provides an interface to libdl, the dynamic loader on UNIX. The underlying interface
has always been used by L<FFI::Platypus>, but it wasn't a public interface until version 0.52. The
name was changed with that version when it became a public interface, so be sure to specify that
version if you are going to use it.
It is somewhat non-portable for these reasons:
=over 4
=item GNU extensions
It provides some GNU extensions to platforms such as Linux that support them.
=item Windows
It provides an emulation layer on Windows. The emulation layer only supports C<RTLD_PLATYPUS_DEFAULT>
as a flag. The emulation layer emulates the convention described below of passing C<undef> as
the dynamic library name to mean, use the currently running executable. I've used it without
any problems for years, but Windows is not my main development platform.
=back
=head1 FUNCTIONS
=head2 dlopen
my $handle = dlopen($filename, $flags);
This opens a dynamic library in the context of the dynamic loader. C<$filename> is the full or
relative path to a dynamic library (usually a C<.so> on Linux and some other UNIXen, a C<.dll> on
Windows and a C<.dylib> on OS X). C<$flags> are flags that can be used to alter the behavior
of the library and the symbols it contains. The return value is an opaque pointer or C<$handle>
which can be used to look up symbols with C<dlsym>. The handle should be closed with C<dlclose>
when you are done with it.
By convention if you pass in C<undef> for the filename, the currently loaded executable will be
used instead of a separate dynamic library. This is the easiest and most portable way to find
the address of symbols in the standard C library. This convention is baked into most UNIXen,
but this capability is emulated in Windows which doesn't come with the capability out of the box.
If there is an error in opening the library then C<undef> will be returned and the diagnostic
for the failure can be retrieved with C<dlerror> as described below.
Not all flags are supported on all platforms. You can test if a flag is available using can:
if(FFI::Platypus::DL->can('RTLD_LAZY'))
{
...
}
Typically where flags are not mutually exclusive, they can be or'd together:
my $handle = dlopen("libfoo.so", RTLD_LAZY | RTLD_GLOBAL);
Check your operating system documentation for detailed descriptions of these flags.
=over 4
=item RTLD_PLATYPUS_DEFAULT
This is the L<FFI::Platypus> default for C<dlopen> (NOTE: NOT the libdl default). This is the only
flag supported on Windows. For historical reasons, this is usually C<RTLD_LAZY> on Unix and C<0> on
Windows.
=item RTLD_LAZY
Perform lazy binding.
=item RTLD_NOW
Resolve all symbols before returning from C<dlopen>. Error if all symbols cannot resolve.
=item RTLD_GLOBAL
Symbols are shared.
=item RTLD_LOCAL
Symbols are NOT shared.
=item RTLD_NODELETE
glibc 2.2 extension.
=item RTLD_NOLOAD
glibc 2.2 extension.
=item RTLD_DEEPBIND
glibc 2.3.4 extension.
=back
=head2 dlsym
my $opaque = dlsym($handle, $symbol);
This looks up the given C<$symbol> in the library pointed to by C<$handle>. If the symbol is found,
the address for that symbol is returned as an opaque pointer. This pointer can be passed into
the L<FFI::Platypus> C<function> and C<attach> methods instead of a function name.
If the symbol cannot be found then C<undef> will be returned and the diagnostic for the failure can
be retrieved with C<dlerror> as described below.
=head2 dlclose
my $status = dlclose($handle);
On success, C<dlclose> returns 0; on error, it returns a nonzero value, and the diagnostic for the
failure can be retrieved with C<dlerror> as described below.
=head2 dlerror
my $error_string = dlerror;
Returns the human readable diagnostic for the reason for the failure for the most recent C<dl>
prefixed function call.
=head1 CAVEATS
Some flags for C<dlopen> are not portable. This module may not be supported platforms added to
L<FFI::Platypus> in the future. It does work as far as I know on all of the currently supported
platforms.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,219 @@
package FFI::Platypus::Function;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
# ABSTRACT: An FFI function object
our $VERSION = '1.34'; # VERSION
use overload '&{}' => sub {
my $ffi = shift;
sub { $ffi->call(@_) };
}, 'bool' => sub {
my $ffi = shift;
return $ffi;
}, fallback => 1;
package FFI::Platypus::Function::Function;
use base qw( FFI::Platypus::Function );
sub attach
{
my($self, $perl_name, $proto) = @_;
my $frame = -1;
my($caller, $filename, $line);
do {
($caller, $filename, $line) = caller(++$frame);
} while( $caller =~ /^FFI::Platypus(|::Function|::Function::Wrapper|::Declare)$/ );
$perl_name = join '::', $caller, $perl_name
unless $perl_name =~ /::/;
$self->_attach($perl_name, "$filename:$line", $proto);
$self;
}
sub sub_ref
{
my($self) = @_;
my $frame = -1;
my($caller, $filename, $line);
do {
($caller, $filename, $line) = caller(++$frame);
} while( $caller =~ /^FFI::Platypus(|::Function|::Function::Wrapper|::Declare)$/ );
$self->_sub_ref("$filename:$line");
}
package FFI::Platypus::Function::Wrapper;
use base qw( FFI::Platypus::Function );
sub new
{
my($class, $function, $wrapper) = @_;
bless [ $function, $wrapper ], $class;
}
sub call
{
my($function, $wrapper) = @{ shift() };
@_ = ($function, @_);
goto &$wrapper;
}
sub attach
{
my($self, $perl_name, $proto) = @_;
my($function, $wrapper) = @{ $self };
unless($perl_name =~ /::/)
{
my $caller;
my $frame = -1;
do { $caller = caller(++$frame) } while( $caller =~ /^FFI::Platypus(|::Declare)$/ );
$perl_name = join '::', $caller, $perl_name
}
my $xsub = $function->sub_ref;
{
my $code = sub {
unshift @_, $xsub;
goto &$wrapper;
};
if(defined $proto)
{
_set_prototype($proto, $code);
}
no strict 'refs';
*{$perl_name} = $code;
}
$self;
}
sub sub_ref
{
my($self) = @_;
my($function, $wrapper) = @{ $self };
my $xsub = $function->sub_ref;
return sub {
unshift @_, $xsub;
goto &$wrapper;
};
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Function - An FFI function object
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
# call directly
my $ffi = FFI::Platypus->new( api => 1 );
my $f = $ffi->function(puts => ['string'] => 'int');
$f->call("hello there");
# attach as xsub and call (faster for repeated calls)
$f->attach('puts');
puts('hello there');
=head1 DESCRIPTION
This class represents an unattached platypus function. For more
context and better examples see L<FFI::Platypus>.
=head1 METHODS
=head2 attach
$f->attach($name);
$f->attach($name, $prototype);
Attaches the function as an xsub (similar to calling attach directly
from an L<FFI::Platypus> instance). You may optionally include a
prototype.
=head2 call
my $ret = $f->call(@arguments);
my $ret = $f->(@arguments);
Calls the function and returns the result. You can also use the
function object B<like> a code reference.
=head2 sub_ref
my $code = $f->sub_ref;
Returns an anonymous code reference. This will usually be faster
than using the C<call> method above.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,86 @@
package FFI::Platypus::Internal;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use base qw( Exporter );
require FFI::Platypus;
_init();
our @EXPORT = grep /^FFI_PL/, keys %FFI::Platypus::Internal::;
# ABSTRACT: For internal use only
our $VERSION = '1.34'; # VERSION
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Internal - For internal use only
=head1 VERSION
version 1.34
=head1 SYNOPSIS
perldoc FFI::Platypus
=head1 DESCRIPTION
This module is for internal use only. Do not rely on it having any particular behavior, or even existing in future versions.
You have been warned.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,93 @@
package FFI::Platypus::Lang;
use strict;
use warnings;
use 5.008004;
# ABSTRACT: Language specific customizations
our $VERSION = '1.34'; # VERSION
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Lang - Language specific customizations
=head1 VERSION
version 1.34
=head1 SYNOPSIS
perldoc FFI::Platypus::Lang;
=head1 DESCRIPTION
This namespace is reserved for language specific customizations of L<FFI::Platypus>.
This usually involves providing native type maps. It can also involve computing
mangled names. The default language is C, and is defined in L<FFI::Platypus::Lang::C>.
This package itself doesn't do anything, it serves only as documentation.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
=item L<FFI::Platypus::Lang::C>
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,112 @@
package FFI::Platypus::Lang::ASM;
use strict;
use warnings;
use 5.008004;
# ABSTRACT: Documentation and tools for using Platypus with the Assembly
our $VERSION = '1.34'; # VERSION
sub native_type_map
{
{}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Lang::ASM - Documentation and tools for using Platypus with the Assembly
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->lang('ASM');
=head1 DESCRIPTION
Setting your lang to C<ASM> includes no native type aliases, so types
like C<int> or C<unsigned long> will not work. You need to specify
instead C<sint32> or C<sint64>. Although intended for use with Assembly
it could also be used for other languages if you did not want to use
the normal C aliases for native types.
This document will one day include information on bundling Assembly
with your Perl / FFI / Platypus distribution. Pull requests welcome!
=head1 METHODS
=head2 native_type_map
my $hashref = FFI::Platypus::Lang::ASM->native_type_map;
This returns an empty hash reference. For other languages it returns
a hash reference that defines the aliases for the types normally used
for that language.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The Core Platypus documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,109 @@
package FFI::Platypus::Lang::C;
use strict;
use warnings;
use 5.008004;
# ABSTRACT: Documentation and tools for using Platypus with the C programming language
our $VERSION = '1.34'; # VERSION
sub native_type_map
{
require FFI::Platypus::ShareConfig;
FFI::Platypus::ShareConfig->get('type_map');
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Lang::C - Documentation and tools for using Platypus with the C programming language
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->lang('C'); # the default
=head1 DESCRIPTION
This module provides some hooks for Platypus to interact with the C
programming language. It is generally used by default if you do not
specify another foreign programming language with the
L<FFI::Platypus#lang> attribute.
=head1 METHODS
=head2 native_type_map
my $hashref = FFI::Platypus::Lang::C->native_type_map;
This returns a hash reference containing the native aliases for the
C programming languages. That is the keys are native C types and the
values are libffi native types.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The Core Platypus documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,272 @@
package FFI::Platypus::Lang::Win32;
use strict;
use warnings;
use 5.008004;
use Config;
# ABSTRACT: Documentation and tools for using Platypus with the Windows API
our $VERSION = '1.34'; # VERSION
sub abi
{
$^O =~ /^(cygwin|MSWin32|msys)$/ && $Config{ptrsize} == 4
? 'stdcall'
: 'default_abi';
}
my %map;
sub native_type_map
{
unless(%map)
{
require FFI::Platypus::ShareConfig;
%map = %{ FFI::Platypus::ShareConfig->get('type_map') };
my %win32_map = qw(
BOOL int
BOOLEAN BYTE
BYTE uchar
CCHAR char
CHAR char
COLORREF DWORD
DWORD uint
DWORDLONG uint64
DWORD_PTR ULONG_PTR
DWORD32 uint32
DWORD64 uint64
FLOAT float
HACCEL HANDLE
HANDLE PVOID
HBITMAP HANDLE
HBRUSH HANDLE
HCOLORSPACE HANDLE
HCONV HANDLE
HCONVLIST HANDLE
HCURSOR HICON
HDC HANDLE
HDDEDATA HANDLE
HDESK HANDLE
HDROP HANDLE
HDWP HANDLE
HENHMETAFILE HANDLE
HFILE int
HFONT HANDLE
HGDIOBJ HANDLE
HGLOBAL HANDLE
HHOOK HANDLE
HICON HANDLE
HINSTANCE HANDLE
HKEY HANDLE
HKL HANDLE
HLOCAL HANDLE
HMENU HANDLE
HMETAFILE HANDLE
HMODULE HINSTANCE
HMONITOR HANDLE
HPALETTE HANDLE
HPEN HANDLE
HRESULT LONG
HRGN HANDLE
HRSRC HANDLE
HSZ HANDLE
HWINSTA HANDLE
HWND HANDLE
INT int
INT8 sint8
INT16 sint16
INT32 sint32
INT64 sint64
LANGID WORD
LCID DWORD
LCTYPE DWORD
LGRPID DWORD
LONG sint32
LONGLONG sint64
LONG32 sint32
LONG64 sint64
LPCSTR string
LPCVOID opaque
LPVOID opaque
LRESULT LONG_PTR
PSTR string
PVOID opaque
QWORD uint64
SC_HANDLE HANDLE
SC_LOCK LPVOID
SERVICE_STATUS_HANDLE HANDLE
SHORT sint16
SIZE_T ULONG_PTR
SSIZE_T LONG_PTR
UCHAR uint8
UINT8 uint8
UINT16 uint16
UINT32 uint32
UINT64 uint64
ULONG uint32
ULONGLONG uint64
ULONG32 uint32
ULONG64 uint64
USHORT uint16
USN LONGLONG
VOID void
WORD uint16
WPARAM UINT_PTR
);
if($Config{ptrsize} == 4)
{
$win32_map{HALF_PTR} = 'sint16';
$win32_map{INT_PTR} = 'sint32';
$win32_map{LONG_PTR} = 'sint16';
$win32_map{UHALF_PTR} = 'uint16';
$win32_map{UINT_PTR} = 'uint32';
$win32_map{ULONG_PTR} = 'uint16';
}
elsif($Config{ptrsize} == 8)
{
$win32_map{HALF_PTR} = 'sint16';
$win32_map{INT_PTR} = 'sint32';
$win32_map{LONG_PTR} = 'sint16';
$win32_map{UHALF_PTR} = 'uint16';
$win32_map{UINT_PTR} = 'uint32';
$win32_map{ULONG_PTR} = 'uint16';
}
else
{
die "interesting word size you have";
}
foreach my $alias (keys %win32_map)
{
my $type = $alias;
while(1)
{
if($type =~ /^(opaque|[us]int(8|16|32|64)|float|double|string|void)$/)
{
$map{$alias} = $type;
last;
}
if(defined $map{$type})
{
$map{$alias} = $map{$type};
last;
}
if(defined $win32_map{$type})
{
$type = $win32_map{$type};
next;
}
die "unable to resolve $alias => ... => $type";
}
}
# stuff we are not yet dealing with
# LPCTSTR is unicode string, not currently supported
# LPWSTR 16 bit unicode string
# TBYTE TCHAR UNICODE_STRING WCHAR
# Not supported: POINTER_32 POINTER_64 POINTER_SIGNED POINTER_UNSIGNED
}
\%map;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Lang::Win32 - Documentation and tools for using Platypus with the Windows API
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->lang('Win32');
=head1 DESCRIPTION
This module provides the Windows datatypes used by the Windows API.
This means that you can use things like C<DWORD> as an alias for
C<uint32>.
=head1 METHODS
=head2 abi
my $abi = FFI::Platypus::Lang::Win32->abi;
=head2 native_type_map
my $hashref = FFI::Platypus::Lang::Win32->native_type_map;
This returns a hash reference containing the native aliases for the
Windows API. That is the keys are native Windows API C types and the
values are libffi native types.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The Core Platypus documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,130 @@
package FFI::Platypus::Legacy;
use strict;
use warnings;
use 5.008004;
# ABSTRACT: Legacy Platypus interfaces
our $VERSION = '1.34'; # VERSION
package FFI::Platypus;
sub _package
{
my($self, $module, $modlibname) = @_;
($module, $modlibname) = caller unless defined $modlibname;
my @modparts = split /::/, $module;
my $modfname = $modparts[-1];
my $modpname = join('/',@modparts);
my $c = @modparts;
$modlibname =~ s,[\\/][^\\/]+$,, while $c--; # Q&D basename
{
my @maybe = (
"$modlibname/auto/$modpname/$modfname.txt",
"$modlibname/../arch/auto/$modpname/$modfname.txt",
);
foreach my $file (@maybe)
{
if(-f $file)
{
open my $fh, '<', $file;
my $line = <$fh>;
close $fh;
if($line =~ /^FFI::Build\@(.*)$/)
{
$self->lib("$modlibname/$1");
return $self;
}
}
}
}
require FFI::Platypus::ShareConfig;
my @dlext = @{ FFI::Platypus::ShareConfig->get("config_dlext") };
foreach my $dlext (@dlext)
{
my $file = "$modlibname/auto/$modpname/$modfname.$dlext";
unless(-e $file)
{
$modlibname =~ s,[\\/][^\\/]+$,,;
$file = "$modlibname/arch/auto/$modpname/$modfname.$dlext";
}
if(-e $file)
{
$self->lib($file);
return $self;
}
}
$self;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Legacy - Legacy Platypus interfaces
=head1 VERSION
version 1.34
=head1 DESCRIPTION
This class is private to L<FFI::Platypus>.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,219 @@
package FFI::Platypus::Memory;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use base qw( Exporter );
# ABSTRACT: Memory functions for FFI
our $VERSION = '1.34'; # VERSION
our @EXPORT = qw( malloc free calloc realloc memcpy memset strdup strndup strcpy );
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->lib(undef);
$ffi->bundle;
sub _ffi { $ffi }
$ffi->attach(malloc => ['size_t'] => 'opaque' => '$');
$ffi->attach(free => ['opaque'] => 'void' => '$');
$ffi->attach(calloc => ['size_t', 'size_t'] => 'opaque' => '$$');
$ffi->attach(realloc => ['opaque', 'size_t'] => 'opaque' => '$$');
$ffi->attach(memcpy => ['opaque', 'opaque', 'size_t'] => 'opaque' => '$$$');
$ffi->attach(memset => ['opaque', 'int', 'size_t'] => 'opaque' => '$$$');
$ffi->attach(strcpy => ['opaque', 'string'] => 'opaque' => '$$');
my $_strdup_impl = 'not-loaded';
sub _strdup_impl { $_strdup_impl }
eval {
die "do not use c impl" if ($ENV{FFI_PLATYPUS_MEMORY_STRDUP_IMPL}||'libc') eq 'ffi';
$ffi->attach(strdup => ['string'] => 'opaque' => '$');
$_strdup_impl = 'libc';
};
if($@)
{
$_strdup_impl = 'ffi';
$ffi->attach([ ffi_platypus_memory__strdup => 'strdup' ] => ['string'] => 'opaque' => '$');
}
my $_strndup_impl = 'not-loaded';
sub _strndup_impl { $_strndup_impl }
eval {
die "do not use c impl" if ($ENV{FFI_PLATYPUS_MEMORY_STRDUP_IMPL}||'libc') eq 'ffi';
$ffi->attach(strndup => ['string','size_t'] => 'opaque' => '$$');
$_strndup_impl = 'libc';
};
if($@)
{
$_strndup_impl = 'ffi';
$ffi->attach([ ffi_platypus_memory__strndup => 'strndup' ] => ['string','size_t'] => 'opaque' => '$$');
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Memory - Memory functions for FFI
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus::Memory;
# allocate 64 bytes of memory using the
# libc malloc function.
my $pointer = malloc 64;
# use that memory wisely
...
# free the memory when you are done.
free $pointer;
=head1 DESCRIPTION
This module provides an interface to common memory functions provided by
the standard C library. They may be useful when constructing interfaces
to C libraries with FFI. It works mostly with the C<opaque> type and it
is worth reviewing the section on opaque pointers in L<FFI::Platypus::Type>.
=head1 FUNCTIONS
=head2 calloc
my $pointer = calloc $count, $size;
The C<calloc> function contiguously allocates enough space for I<$count>
objects that are I<$size> bytes of memory each.
=head2 free
free $pointer;
The C<free> function frees the memory allocated by C<malloc>, C<calloc>,
C<realloc> or C<strdup>. It is important to only free memory that you
yourself have allocated. A good way to crash your program is to try and
free a pointer that some C library has returned to you.
=head2 malloc
my $pointer = malloc $size;
The C<malloc> function allocates I<$size> bytes of memory.
=head2 memcpy
memcpy $dst_pointer, $src_pointer, $size;
The C<memcpy> function copies I<$size> bytes from I<$src_pointer> to
I<$dst_pointer>. It also returns I<$dst_pointer>.
=head2 memset
memset $buffer, $value, $length;
The C<memset> function writes I<$length> bytes of I<$value> to the address
specified by I<$buffer>.
=head2 realloc
my $new_pointer = realloc $old_pointer, $size;
The C<realloc> function reallocates enough memory to fit I<$size> bytes.
It copies the existing data and frees I<$old_pointer>.
If you pass C<undef> in as I<$old_pointer>, then it behaves exactly like
C<malloc>:
my $pointer = realloc undef, 64; # same as malloc 64
=head2 strcpy
strcpy $opaque, $string;
Copies the string to the memory location pointed to by C<$opaque>.
=head2 strdup
my $pointer = strdup $string;
The C<strdup> function allocates enough memory to contain I<$string> and
then copies it to that newly allocated memory. This version of
C<strdup> returns an opaque pointer type, not a string type. This may
seem a little strange, but returning a string type would not be very
useful in Perl.
=head2 strndup
my $pointer = strndup $string, $max;
The same as C<strdup> above, except at most C<$max> characters will be
copied in the new string.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
Main Platypus documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,557 @@
package FFI::Platypus::Record;
use strict;
use warnings;
use 5.008004;
use Carp qw( croak );
use FFI::Platypus;
use base qw( Exporter );
use constant 1.32 ();
our @EXPORT = qw( record_layout record_layout_1 );
# ABSTRACT: FFI support for structured records data
our $VERSION = '1.34'; # VERSION
sub record_layout_1
{
if(@_ % 2 == 0)
{
my $ffi = FFI::Platypus->new( api => 1 );
unshift @_, $ffi;
goto &record_layout;
}
elsif(defined $_[0] && ref($_[0]) eq 'ARRAY')
{
my @args = @{ shift @_ };
unshift @args, api => 1;
unshift @_, \@args;
goto &record_layout;
}
elsif(defined $_[0] && eval { $_[0]->isa('FFI::Platypus') })
{
goto &record_layout;
}
else
{
croak "odd number of arguments, but first argument is not either an array reference or Platypus instance";
}
}
sub record_layout
{
my $ffi;
if(defined $_[0])
{
if(ref($_[0]) eq 'ARRAY')
{
my @args = @{ shift() };
$ffi = FFI::Platypus->new(@args);
}
elsif(eval { $_[0]->isa('FFI::Platypus') })
{
$ffi = shift;
}
}
$ffi ||= FFI::Platypus->new;
my $offset = 0;
my $record_align = 0;
croak "uneven number of arguments!" if scalar(@_) % 2;
my($caller, $filename, $line) = caller;
if($caller->can("_ffi_record_size")
|| $caller->can("ffi_record_size"))
{
croak "record already defined for the class $caller";
}
my @destroy;
my @ffi_types;
while(@_)
{
my $spec = shift;
my $name = shift;
my $type = $ffi->{tp}->parse( $spec, { member => 1 } );
croak "illegal name $name"
unless $name =~ /^[A-Za-z_][A-Za-z_0-9]*$/
|| $name eq ':';
croak "accessor/method $name already exists"
if $caller->can($name);
my $size = $type->sizeof;
my $align = $type->alignof;
$record_align = $align if $align > $record_align;
my $meta = $type->meta;
$offset++ while $offset % $align;
{
my $count;
my $ffi_type;
if($meta->{type} eq 'record') # this means fixed string atm
{
$ffi_type = 'sint8';
$count = $size;
}
else
{
$ffi_type = $meta->{ffi_type};
$count = $meta->{element_count};
$count = 1 unless defined $count;
}
push @ffi_types, $ffi_type for 1..$count;
}
if($name ne ':')
{
if($meta->{type} eq 'string'
&& $meta->{access} eq 'rw')
{
push @destroy, eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) .qq{
sub {
shift->$name(undef);
};
};
die $@ if $@;
}
my $full_name = join '::', $caller, $name;
my $error_str = _accessor
$full_name,
"$filename:$line",
$type,
$offset;
croak("$error_str ($spec $name)") if $error_str;
};
$offset += $size;
}
my $size = $offset;
no strict 'refs';
constant->import("${caller}::_ffi_record_size", $size);
constant->import("${caller}::_ffi_record_align", $record_align);
*{join '::', $caller, '_ffi_record_ro'} = \&_ffi_record_ro;
*{join '::', $caller, 'new'} = sub {
my $class = shift;
my $args = ref($_[0]) ? [%{$_[0]}] : \@_;
croak "uneven number of arguments to record constructor"
if @$args % 2;
my $record = "\0" x $class->_ffi_record_size;
my $self = bless \$record, $class;
while(@$args)
{
my $key = shift @$args;
my $value = shift @$args;
$self->$key($value);
}
$self;
};
{
require FFI::Platypus::Record::Meta;
my $ffi_meta = FFI::Platypus::Record::Meta->new(
\@ffi_types,
);
*{join '::', $caller, '_ffi_meta'} = sub { $ffi_meta };
}
my $destroy_sub = sub {};
if(@destroy)
{
$destroy_sub = sub {
return if _ffi_record_ro($_[0]);
$_->($_[0]) for @destroy;
};
}
do {
no strict 'refs';
*{"${caller}::DESTROY"} = $destroy_sub;
};
();
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Record - FFI support for structured records data
=head1 VERSION
version 1.34
=head1 SYNOPSIS
C:
struct my_person {
int age;
const char title[3];
const char *name
};
void process_person(struct my_person *person)
{
/* ... */
}
Perl:
package MyPerson;
use FFI::Platypus::Record;
record_layout_1(qw(
int age
string(3) title
string_rw name
));
package main;
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->lib("myperson.so");
$ffi->type("record(MyPerson)" => 'MyPerson');
my $person = MyPerson->new(
age => 40,
title => "Mr.",
name => "John Smith",
);
$ffi->attach( process_person => [ 'MyPerson*' ] => 'void' );
process_person($person);
$person->age($person->age + 1); # another year older
process_person($person);
=head1 DESCRIPTION
[version 0.21]
This module provides a mechanism for building classes that can be used
to mange structured data records (known as C as "structs" and in some
languages as "records"). A structured record is a series of bytes that
have structure understood by the C or other foreign language library
that you are interfacing with. It is designed for use with FFI and
L<FFI::Platypus>, though it may have other applications.
Before you get to deep into using this class you should also consider
the L<FFI::C>, which provides some overlapping functionality. Briefly,
it comes down to this:
=over 4
=item L<FFI::Platypus::Record>
Supports:
=over 4
=item C pointers to C<struct> types
=item Passing C <struct>s by-value.
=back
Does not support:
=over 4
=item C C<union> types.
=item C arrays of C<struct> and C<union> types.
=back
=item L<FFI::C>
Supports:
=over 4
=item C C<struct> andC<union> types
=item C arrays of C<struct> and C<union> types.
=back
Does not support:
=over 4
=item Passing C C<struct>s by-value.
=back
String members are as of this writing a TODO for L<FFI::C>, but
should be coming soon!
=back
=head1 FUNCTIONS
=head2 record_layout_1
record_layout_1($ffi, $type => $name, ... );
record_layout_1(\@ffi_args, $type => $name, ... );
record_layout_1($type => $name, ... );
Define the layout of the record. You may optionally provide an instance
of L<FFI::Platypus> as the first argument in order to use its type
aliases. Alternatively you may provide constructor arguments that will
be passed to the internal platypus instance. Thus this is the same:
my $ffi = FFI::Platypus->new( lang => 'Rust', api => 1 );
record_layout_1( $ffi, ... );
# same as:
record_layout_1( [ lang => 'Rust' ], ... );
and this is the same:
my $ffi = FFI::Platypus->new( api => 1 );
record_layout_1( $ffi, ... );
# same as:
record_layout_1( ... );
Then you provide members as type/name pairs.
For each member you declare, C<record_layout_1> will create an accessor
which can be used to read and write its value. For example imagine a
class C<Foo>:
package Foo;
use FFI::Platypus::Record;
record_layout_1(
int => 'bar', # int bar;
'string(10)' => 'baz', # char baz[10];
);
You can get and set its fields with like named C<bar> and C<baz>
accessors:
my $foo = Foo->new;
$foo->bar(22);
my $value = $foo->bar;
$foo->baz("grimlock\0\0"); # should be 10 characters long
my $string_value = $foo->baz; # includes the trailing \0\0
You can also pass initial values in to the constructor, either passing
as a list of key value pairs or by passing a hash reference:
$foo = Foo->new(
bar => 22,
baz => "grimlock\0\0",
);
# same as:
$foo = Foo->new( {
bar => 22,
baz => "grimlock\0\0",
} );
If there are members of a record that you need to account for in terms
of size and alignment, but do not want to have an accessor for, you can
use C<:> as a place holder for its name:
record_layout_1(
'int' => ':',
'string(10)' => 'baz',
);
=head3 strings
So far I've shown fixed length strings. These are declared with the
word C<string> followed by the length of the string in parentheticals.
Fixed length strings are included inside the record itself and do not
need to be allocated or deallocated separately from the record.
Variable length strings must be allocated on the heap, and thus require
a sense of "ownership", that is whomever allocates variable length
strings should be responsible for also free'ing them. To handle this,
you can add a C<ro> or C<rw> trait to a string field. The default is
C<ro>, means that you can get, but not set its value:
package Foo;
record_layout_1(
'string ro' => 'bar', # same type as 'string' and 'string_ro'
);
package main;
my $foo = Foo->new;
my $string = $foo->bar; # GOOD
$foo->bar("starscream"); # BAD
If you specify a field is C<rw>, then you can set its value:
package Foo;
record_layout_1(
'string rw' => 'bar', # same type as 'string_rw'
);
package main;
my $foo = Foo->new;
my $string = $foo->bar; # GOOD
$foo->bar("starscream"); # GOOD
Any string value that is pointed to by the record will be free'd when it
falls out of scope, so you must be very careful that any C<string rw>
fields are not set or modified by C code. You should also take care not
to copy any record that has a C<rw> string in it because its values will
be free'd twice!
use Clone qw( clone );
my $foo2 = clone $foo; # BAD bar will be free'd twice
=head3 arrays
Arrays of integer, floating points and opaque pointers are supported.
package Foo;
record_layout_1(
'int[10]' => 'bar',
);
my $foo = Foo->new;
$foo->bar([1,2,3,4,5,6,7,8,9,10]); # sets the values for the array
my $list = $foo->bar; # returns a list reference
$foo->bar(5, -6); # sets the 5th element in the array to -6
my $item = $foo->bar(5); gets the 5th element in the array
=head2 record_layout
record_layout($ffi, $type => $name, ... );
record_layout(\@ffi_args, $type => $name, ... );
record_layout($type => $name, ... );
This function works like C<record_layout> except that
C<api =E<gt> 0> is used instead of C<api =E<gt> 1>.
All new code should use C<record_layout_1> instead.
=head1 CAVEATS
These useful features (and probably more) are missing, and unlikely to be added.
=over 4
=item Unions
=item Nested records
=back
If you need these features, consider using L<FFI::C> instead.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The main platypus documentation.
=item L<FFI::C>
Another interface for constructing structured data. It includes support for
C<union> and array types (which this module does not), but lacks support for
passing records by-value.
=item L<FFI::Platypus::Record::TieArray>
Tied array interface for record array members.
=item L<Convert::Binary::C>
Another method for constructing and dissecting structured data records.
=item L<pack and unpack|perlpacktut>
Built-in Perl functions for constructing and dissecting structured data
records.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,136 @@
package FFI::Platypus::Record::Meta;
use strict;
use warnings;
use 5.008004;
# ABSTRACT: FFI support for structured records data
our $VERSION = '1.34'; # VERSION
{
require FFI::Platypus;
my $ffi = FFI::Platypus->new(
api => 1,
);
$ffi->bundle;
$ffi->mangler(sub {
my($name) = @_;
$name =~ s/^/ffi_platypus_record_meta__/;
$name;
});
$ffi->type('opaque' => 'ffi_type');
$ffi->custom_type('meta_t' => {
native_type => 'opaque',
perl_to_native => sub {
${ $_[0] };
},
});
$ffi->attach( _find_symbol => ['string'] => 'ffi_type');
$ffi->attach( new => ['ffi_type[]'] => 'meta_t', sub {
my($xsub, $class, $elements) = @_;
if(ref($elements) ne 'ARRAY')
{
require Carp;
Carp::croak("passed something other than a array ref to @{[ __PACKAGE__ ]}");
}
my @element_type_pointers;
foreach my $element_type (@$elements)
{
my $ptr = _find_symbol($element_type);
if($ptr)
{
push @element_type_pointers, $ptr;
}
else
{
require Carp;
Carp::croak("unknown type: $element_type");
}
}
push @element_type_pointers, undef;
my $ptr = $xsub->(\@element_type_pointers);
bless \$ptr, $class;
});
$ffi->attach( ffi_type => ['meta_t'] => 'ffi_type' );
$ffi->attach( size => ['meta_t'] => 'size_t' );
$ffi->attach( alignment => ['meta_t'] => 'ushort' );
$ffi->attach( element_pointers => ['meta_t'] => 'ffi_type[]' );
$ffi->attach( DESTROY => ['meta_t'] => 'void' );
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Record::Meta - FFI support for structured records data
=head1 VERSION
version 1.34
=head1 DESCRIPTION
This class is private to FFI::Platypus. See L<FFI::Platypus::Record> for
the public interface to Platypus records.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,164 @@
package FFI::Platypus::Record::TieArray;
use strict;
use warnings;
use 5.008004;
use Carp qw( croak );
# ABSTRACT: Tied array interface for record array members
our $VERSION = '1.34'; # VERSION
sub TIEARRAY
{
my $class = shift;
bless [ @_ ], $class;
}
sub FETCH
{
my($self, $key) = @_;
my($obj, $member) = @$self;
$obj->$member($key);
}
sub STORE
{
my($self, $key, $value) = @_;
my($obj, $member) = @$self;
$obj->$member($key, $value);
}
sub FETCHSIZE
{
my($self) = @_;
$self->[2];
}
sub CLEAR
{
my($self) = @_;
my($obj, $member) = @$self;
$obj->$member([]);
}
sub EXTEND
{
my($self, $count) = @_;
croak "tried to extend a fixed length array" if $count > $self->[2];
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Record::TieArray - Tied array interface for record array members
=head1 VERSION
version 1.34
=head1 SYNOPSIS
package Foo;
use FFI::Platypus::Record;
use FFI::Platypus::Record::TieArray;
record_layout(qw(
int[20] _bar
));
sub bar
{
my($self, $arg) = @_;
$self->_bar($arg) if ref($arg) eq ' ARRAY';
tie my @list, 'FFI::Platypus::Record::TieArray',
$self, '_bar', 20;
}
package main;
my $foo = Foo->new;
my $bar5 = $foo->bar->[5]; # get the 5th element of the bar array
$foo->bar->[5] = 10; # set the 5th element of the bar array
@{ $foo->bar } = (); # set all elements in bar to 0
@{ $foo->bar } = (1..5); # set the first five elements of the bar array
=head1 DESCRIPTION
B<WARNING>: This module is considered EXPERIMENTAL. It may go away or
be changed in incompatible ways, possibly without notice, but not
without a good reason.
This class provides a tie interface for record array members.
In the future a short cut for using this with L<FFI::Platypus::Record>
directly may be provided.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The main Platypus documentation.
=item L<FFI::Platypus::Record>
Documentation on Platypus records.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,112 @@
package FFI::Platypus::ShareConfig;
use strict;
use warnings;
use 5.008004;
use File::Spec;
our $VERSION = '1.34'; # VERSION
sub dist_dir ($)
{
my($dist_name) = @_;
my @pm = split /-/, $dist_name;
$pm[-1] .= ".pm";
foreach my $inc (@INC)
{
if(-f File::Spec->catfile($inc, @pm))
{
my $share = File::Spec->catdir($inc, qw( auto share dist ), $dist_name );
if(-d $share)
{
return File::Spec->rel2abs($share);
}
last;
}
}
Carp::croak("unable to find dist share directory for $dist_name");
}
sub get
{
my(undef, $name) = @_;
my $config;
unless($config)
{
my $fn = File::Spec->catfile(dist_dir('FFI-Platypus'), 'config.pl');
$fn = File::Spec->rel2abs($fn) unless File::Spec->file_name_is_absolute($fn);
local $@;
unless($config = do $fn)
{
die "couldn't parse configuration $fn $@" if $@;
die "couldn't do $fn $!" if $!;
die "bad or missing config file $fn";
};
}
defined $name ? $config->{$name} : $config;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::ShareConfig
=head1 VERSION
version 1.34
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
package FFI::Platypus::Type::PointerSizeBuffer;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use FFI::Platypus::API qw(
arguments_set_pointer
arguments_set_uint32
arguments_set_uint64
);
use FFI::Platypus::Buffer qw( scalar_to_buffer );
use FFI::Platypus::Buffer qw( buffer_to_scalar );
# ABSTRACT: Convert string scalar to a buffer as a pointer / size_t combination
our $VERSION = '1.34'; # VERSION
my @stack;
*arguments_set_size_t
= FFI::Platypus->new( api => 1 )->sizeof('size_t') == 4
? \&arguments_set_uint32
: \&arguments_set_uint64;
sub perl_to_native
{
my($pointer, $size) = scalar_to_buffer($_[0]);
push @stack, [ $pointer, $size ];
arguments_set_pointer $_[1], $pointer;
arguments_set_size_t($_[1]+1, $size);
}
sub perl_to_native_post
{
my($pointer, $size) = @{ pop @stack };
$_[0] = buffer_to_scalar($pointer, $size);
}
sub ffi_custom_type_api_1
{
{
native_type => 'opaque',
perl_to_native => \&perl_to_native,
perl_to_native_post => \&perl_to_native_post,
argument_count => 2,
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Type::PointerSizeBuffer - Convert string scalar to a buffer as a pointer / size_t combination
=head1 VERSION
version 1.34
=head1 SYNOPSIS
In your C code:
void
function_with_buffer(void *pointer, size_t size)
{
...
}
In your Platypus::FFI code:
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->load_custom_type('::PointerSizeBuffer' => 'buffer');
$ffi->attach(function_with_buffer => ['buffer'] => 'void');
my $string = "content of buffer";
function_with_buffer($string);
=head1 DESCRIPTION
A common pattern in C code is to pass in a region of memory as a buffer,
consisting of a pointer and a size of the memory region. In Perl,
string scalars also point to a contiguous series of bytes that has a
size, so when interfacing with C libraries it is handy to be able to
pass in a string scalar as a pointer / size buffer pair.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
Main Platypus documentation.
=item L<FFI::Platypus::Type>
Platypus types documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,262 @@
package FFI::Platypus::Type::StringArray;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
# ABSTRACT: Platypus custom type for arrays of strings
our $VERSION = '1.34'; # VERSION
use constant _incantation =>
$^O eq 'MSWin32' && do { require Config; $Config::Config{archname} =~ /MSWin32-x64/ }
? 'Q'
: 'L!';
use constant _size_of_pointer => FFI::Platypus->new( api => 1 )->sizeof('opaque');
use constant _pointer_buffer => "P" . _size_of_pointer;
my @stack;
sub perl_to_native
{
# this is the variable length version
# and is actually simpler than the
# fixed length version
my $count = scalar @{ $_[0] };
my $pointers = pack(('P' x $count)._incantation, @{ $_[0] }, 0);
my $array_pointer = unpack _incantation, pack 'P', $pointers;
push @stack, [ \$_[0], \$pointers ];
$array_pointer;
}
sub perl_to_native_post
{
pop @stack;
();
}
sub native_to_perl
{
return unless defined $_[0];
my @list;
my $i=0;
while(1)
{
my $pointer_pointer = unpack(
_incantation,
unpack(
_pointer_buffer,
pack(
_incantation, $_[0]+_size_of_pointer*$i
)
)
);
last unless $pointer_pointer;
push @list, unpack('p', pack(_incantation, $pointer_pointer));
$i++;
}
\@list;
}
sub ffi_custom_type_api_1
{
# arg0 = class
# arg1 = FFI::Platypus instance
# arg2 = array size
# arg3 = default value
my(undef, undef, $count, $default) = @_;
my $config = {
native_type => 'opaque',
perl_to_native => \&perl_to_native,
perl_to_native_post => \&perl_to_native_post,
native_to_perl => \&native_to_perl,
};
if(defined $count)
{
my $end = $count-1;
$config->{perl_to_native} = sub {
my $incantation = '';
my @list = ((map {
defined $_
? do { $incantation .= 'P'; $_ }
: defined $default
? do { $incantation .= 'P'; $default }
: do { $incantation .= _incantation; 0 };
} @{ $_[0] }[0..$end]), 0);
$incantation .= _incantation;
my $pointers = pack $incantation, @list;
my $array_pointer = unpack _incantation, pack 'P', $pointers;
push @stack, [ \@list, $pointers ];
$array_pointer;
};
my $pointer_buffer = "P@{[ FFI::Platypus->new( api => 1 )->sizeof('opaque') * $count ]}";
my $incantation_count = _incantation.$count;
$config->{native_to_perl} = sub {
return unless defined $_[0];
my @pointer_pointer = unpack($incantation_count, unpack($pointer_buffer, pack(_incantation, $_[0])));
[map { $_ ? unpack('p', pack(_incantation, $_)) : $default } @pointer_pointer];
};
}
$config;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Type::StringArray - Platypus custom type for arrays of strings
=head1 VERSION
version 1.34
=head1 SYNOPSIS
In your C code:
void
takes_string_array(const char **array)
{
...
}
void
takes_fixed_string_array(const char *array[5])
{
...
}
In your L<Platypus::FFI> code:
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->load_custom_type('::StringArray' => 'string_array');
$ffi->load_custom_type('::StringArray' => 'string_5' => 5);
$ffi->attach(takes_string_array => ['string_array'] => 'void');
$ffi->attach(takes_fixed_string_array => ['string_5'] => 'void');
my @list = qw( foo bar baz );
takes_string_array(\@list);
takes_fixed_string_array([qw( s1 s2 s3 s4 s5 )]);
=head1 DESCRIPTION
B<NOTE>: The primary motivation for this custom type was originally to
fill the void left by the fact that L<FFI::Platypus> did not support arrays
of strings by itself. Since 0.62 this support has been added, and that is
probably what you want to use, but the semantics and feature set are
slightly different, so there are cases where you might want to use this
custom type.
This module provides a L<FFI::Platypus> custom type for arrays of
strings. The array is always NULL terminated. Return types are supported!
This custom type takes two optional arguments. The first is the size of
arrays and the second is a default value to fill in any values that
aren't provided when the function is called. If not default is provided
then C<NULL> will be passed in for those values.
=head1 SUPPORT
If something does not work the way you think it should, or if you have a
feature request, please open an issue on this project's GitHub Issue
tracker:
L<https://github.com/plicease/FFI-Platypus-Type-StringArray/issues>
=head1 CONTRIBUTING
If you have implemented a new feature or fixed a bug then you may make a
pull request on this project's GitHub repository:
L<https://github.com/plicease/FFI-Platypus-Type-StringArray/pulls>
This project's GitHub issue tracker listed above is not Write-Only. If
you want to contribute then feel free to browse through the existing
issues and see if there is something you feel you might be good at and
take a whack at the problem. I frequently open issues myself that I
hope will be accomplished by someone in the future but do not have time
to immediately implement myself.
Another good area to help out in is documentation. I try to make sure
that there is good document coverage, that is there should be
documentation describing all the public features and warnings about
common pitfalls, but an outsider's or alternate view point on such
things would be welcome; if you see something confusing or lacks
sufficient detail I encourage documentation only pull requests to
improve things.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
=item L<FFI::Platypus::Type::StringPointer>
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,180 @@
package FFI::Platypus::Type::StringPointer;
use strict;
use warnings;
use 5.008004;
use FFI::Platypus;
use Scalar::Util qw( readonly );
# ABSTRACT: Convert a pointer to a string and back
our $VERSION = '1.34'; # VERSION
use constant _incantation =>
$^O eq 'MSWin32' && do { require Config; $Config::Config{archname} =~ /MSWin32-x64/ }
? 'Q'
: 'L!';
use constant _pointer_buffer => "P" . FFI::Platypus->new( api => 1 )->sizeof('opaque');
my @stack;
sub perl_to_native
{
if(defined $_[0])
{
my $packed = pack 'P', ${$_[0]};
my $pointer_pointer = pack 'P', $packed;
my $unpacked = unpack _incantation, $pointer_pointer;
push @stack, [ \$packed, \$pointer_pointer ];
return $unpacked;
}
else
{
push @stack, [];
return undef;
}
}
sub perl_to_native_post
{
my($packed) = @{ pop @stack };
return unless defined $packed;
unless(readonly(${$_[0]}))
{
${$_[0]} = unpack 'p', $$packed;
}
}
sub native_to_perl
{
return unless defined $_[0];
my $pointer_pointer = unpack(_incantation, unpack(_pointer_buffer, pack(_incantation, $_[0])));
$pointer_pointer ? \unpack('p', pack(_incantation, $pointer_pointer)) : \undef;
}
sub ffi_custom_type_api_1
{
return {
native_type => 'opaque',
perl_to_native => \&perl_to_native,
perl_to_native_post => \&perl_to_native_post,
native_to_perl => \&native_to_perl,
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::Type::StringPointer - Convert a pointer to a string and back
=head1 VERSION
version 1.34
=head1 SYNOPSIS
In your C code:
void
string_pointer_argument(const char **string)
{
...
}
const char **
string_pointer_return(void)
{
...
}
In your Platypus::FFI code:
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->load_custom_type('::StringPointer' => 'string_pointer');
$ffi->attach(string_pointer_argument => ['string_pointer'] => 'void');
$ffi->attach(string_pointer_return => [] => 'string_pointer');
my $string = "foo";
string_pointer_argument(\$string); # $string may be modified
$ref = string_pointer_return();
print $$ref; # print the string pointed to by $ref
=head1 DESCRIPTION
B<NOTE>: As of version 0.61, this custom type is now deprecated since
pointers to strings are supported in the L<FFI::Platypus> directly
without custom types.
This module provides a L<FFI::Platypus> custom type for pointers to
strings.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
Main Platypus documentation.
=item L<FFI::Platypus::Type>
Platypus types documentation.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,165 @@
package FFI::Platypus::TypeParser;
use strict;
use warnings;
use 5.008004;
use List::Util 1.45 qw( uniqstr );
use Carp qw( croak );
# ABSTRACT: FFI Type Parser
our $VERSION = '1.34'; # VERSION
# The TypeParser and Type classes are used internally ONLY and
# are not to be exposed to the user. External users should
# not under any circumstances rely on the implementation of
# these classes.
sub new
{
my($class) = @_;
my $self = bless { types => {}, type_map => {} }, $class;
$self->build;
$self;
}
sub build {}
our %basic_type;
# this just checks if the underlying libffi/platypus implementation
# has the basic type. It is used mainly to verify that exotic types
# like longdouble and complex_float are available before the test
# suite tries to use them.
sub have_type
{
my(undef, $name) = @_;
!!$basic_type{$name};
}
sub create_type_custom
{
my($self, $name, @rest) = @_;
$name = 'opaque' unless defined $name;
my $type = $self->parse($name);
unless($type->is_customizable)
{
croak "$name is not a legal basis for a custom type"
}
$self->_create_type_custom($type, @rest);
}
# this is the type map provided by the language plugin, if any
# in addition to the basic types (which map to themselves).
sub type_map
{
my($self, $new) = @_;
if(defined $new)
{
$self->{type_map} = $new;
}
$self->{type_map};
}
# this stores the types that have been mentioned so far. It also
# usually includes aliases.
sub types
{
shift->{types};
}
{
my %store;
foreach my $name (keys %basic_type)
{
my $type_code = $basic_type{$name};
$store{basic}->{$name} = __PACKAGE__->create_type_basic($type_code);
$store{ptr}->{$name} = __PACKAGE__->create_type_pointer($type_code);
$store{rev}->{$type_code} = $name;
}
sub global_types
{
\%store;
}
}
# list all the types that this type parser knows about, including
# those provided by the language plugin (if any), those defined
# by the user, and the basic types that everyone gets.
sub list_types
{
my($self) = @_;
uniqstr( ( keys %{ $self->type_map } ), ( keys %{ $self->types } ) );
}
our @CARP_NOT = qw( FFI::Platypus );
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::TypeParser - FFI Type Parser
=head1 VERSION
version 1.34
=head1 DESCRIPTION
This class is private to FFI::Platypus. See L<FFI::Platypus> for
the public interface to Platypus types.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,275 @@
package FFI::Platypus::TypeParser::Version0;
use strict;
use warnings;
use 5.008004;
use Carp qw( croak );
use base qw( FFI::Platypus::TypeParser );
# ABSTRACT: FFI Type Parser Version Zero
our $VERSION = '1.34'; # VERSION
our @CARP_NOT = qw( FFI::Platypus FFI::Platypus::TypeParser );
# The type parser is responsible for deciding if something is a legal
# alias name. Since this needs to be checked before the type is parsed
# it is separate from set_alias below.
sub check_alias
{
my($self, $alias) = @_;
croak "spaces not allowed in alias" if $alias =~ /\s/;
croak "allowed characters for alias: [A-Za-z0-9_]" if $alias !~ /^[A-Za-z0-9_]+$/;
croak "alias \"$alias\" conflicts with existing type"
if defined $self->type_map->{$alias}
|| $self->types->{$alias};
return 1;
}
sub set_alias
{
my($self, $alias, $type) = @_;
$self->types->{$alias} = $type;
}
# This method takes a string representation of the a type and
# returns the internal platypus type representation.
sub parse
{
my($self, $name) = @_;
return $self->types->{$name} if defined $self->types->{$name};
# Darmock and Legacy Code at Tanagra
unless($name =~ /-\>/ || $name =~ /^record\s*\([0-9A-Z:a-z_]+\)$/
|| $name =~ /^string(_rw|_ro|\s+rw|\s+ro|\s*\([0-9]+\))$/)
{
my $basic = $name;
my $extra = '';
if($basic =~ s/\s*((\*|\[|\<).*)$//)
{
$extra = " $1";
}
if(defined $self->type_map->{$basic})
{
my $new_name = $self->type_map->{$basic} . $extra;
if($new_name ne $name)
{
# hopefully no recursion here.
return $self->types->{$name} = $self->parse($new_name);
}
}
}
if($name =~ m/^ \( (.*) \) \s* -\> \s* (.*) \s* $/x)
{
my @argument_types = map { $self->parse($_) } map { my $t = $_; $t =~ s/^\s+//; $t =~ s/\s+$//; $t } split /,/, $1;
my $return_type = $self->parse($2);
return $self->types->{$name} = $self->create_type_closure($return_type, @argument_types);
}
if($name =~ /^ string \s* \( ([0-9]+) \) $/x)
{
return $self->types->{$name} = $self->create_type_record(
0,
$1, # size
);
}
if($name =~ /^ string ( _rw | _ro | \s+ro | \s+rw | ) $/x)
{
return $self->types->{$name} = $self->create_type_string(
defined $1 && $1 =~ /rw/ ? 1 : 0, # rw
);
}
if($name =~ /^ record \s* \( ([0-9]+) \) $/x)
{
return $self->types->{$name} = $self->create_type_record(
0,
$1, # size
);
}
if($name =~ /^ record \s* \( ([0-9:A-Za-z_]+) \) $/x)
{
my $size;
my $classname = $1;
unless($classname->can('ffi_record_size') || $classname->can('_ffi_record_size'))
{
my $pm = "$classname.pm";
$pm =~ s/\//::/g;
require $pm;
}
if($classname->can('ffi_record_size'))
{
$size = $classname->ffi_record_size;
}
elsif($classname->can('_ffi_record_size'))
{
$size = $classname->_ffi_record_size;
}
else
{
croak "$classname has not ffi_record_size or _ffi_record_size method";
}
return $self->global_types->{record}->{$classname} ||= $self->create_type_record(
0,
$size, # size
$classname, # record_class
);
}
# array types
if($name =~ /^([\S]+)\s+ \[ ([0-9]*) \] $/x)
{
my $size = $2 || '';
my $basic = $self->global_types->{basic}->{$1} || croak("unknown ffi/platypus type $name [$size]");
if($size)
{
return $self->types->{$name} = $self->create_type_array(
$basic->type_code,
$size,
);
}
else
{
return $self->global_types->{array}->{$name} ||= $self->create_type_array(
$basic->type_code,
0
);
}
}
# pointer types
if($name =~ s/\s+\*$//)
{
return $self->global_types->{ptr}->{$name} || croak("unknown ffi/platypus type $name *");
}
# basic types
return $self->global_types->{basic}->{$name} || croak("unknown ffi/platypus type $name");
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::TypeParser::Version0 - FFI Type Parser Version Zero
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 0 );
$ffi->type('record(Foo::Bar)' => 'foo_bar_t');
$ffi->type('opaque' => 'baz_t');
$ffi->type('opaque*' => 'baz_ptr');
=head1 DESCRIPTION
This documents the original L<FFI::Platypus> type parser. It was the default and only
type parser used by L<FFI::Platypus> starting with version C<0.02>. Starting with
version C<1.00> L<FFI::Platypus> comes with a new type parser with design fixes that
are not backward compatibility.
=head2 Interface differences
=over
=item Pass-by-value records are not allowed
Originally L<FFI::Platypus> only supported passing records as a pointer. The type
C<record(Foo::Bar)> actually passes a pointer to the record. In the version 1.00 parser
allows C<record(Foo::Bar)> which is pass-by-value (the contents of the record is copied
onto the stack) and C<record(Foo::Bar)*> which is pass-by-reference or pointer (a pointer
to the record is passed to the callee so that it can make modifications to the record).
TL;DR C<record(Foo::Bar)> in version 0 is equivalent to C<record(Foo::Bar)*> in the
version 1 API. There is no equivalent to C<record(Foo::Bar)*> in the version 0 API.
=item decorate aliases of basic types
This is not allowed in the version 0 API:
$ffi->type('opaque' => 'foo_t'); # ok!
$ffi->type('foo_t*' => 'foo_ptr'); # not ok! in version 0, ok! in version 1
Instead you need to use the basic type in the second type definition:
$ffi->type('opaque' => 'foo_t'); # ok!
$ffi->type('opaque*' => 'foo_ptr'); # ok!
=item object types are not allowed
$ffi->type('object(Foo::Bar)'); # not ok! in version 0, ok! in version 1
=back
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The core L<FFI::Platypus> documentation.
=item L<FFI::Platypus::TypeParser::Version1>
The API C<1.00> type parser.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,371 @@
package FFI::Platypus::TypeParser::Version1;
use strict;
use warnings;
use 5.008004;
use Carp qw( croak );
use base qw( FFI::Platypus::TypeParser );
# ABSTRACT: FFI Type Parser Version One
our $VERSION = '1.34'; # VERSION
our @CARP_NOT = qw( FFI::Platypus FFI::Platypus::TypeParser );
my %reserved = map { $_ => 1 } qw(
string
object
type
role
union
class
struct
record
array
senum
enum
);
# The type parser is responsible for deciding if something is a legal
# alias name. Since this needs to be checked before the type is parsed
# it is separate from set_alias below.
sub check_alias
{
my($self, $alias) = @_;
croak "spaces not allowed in alias" if $alias =~ /\s/;
croak "allowed characters for alias: [A-Za-z0-9_]" if $alias !~ /^[A-Za-z0-9_]+$/;
croak "reserved world \"$alias\" cannot be used as an alias"
if $reserved{$alias};
croak "alias \"$alias\" conflicts with existing type"
if defined $self->type_map->{$alias}
|| $self->types->{$alias}
|| $self->global_types->{basic}->{$alias};
return 1;
}
sub set_alias
{
my($self, $alias, $type) = @_;
$self->types->{$alias} = $type;
}
use constant type_regex =>
qr/^ #
#
\s* # prefix white space
#
(?: #
#
\( ([^)]*) \) -> (.*) # closure $1 argument types, $2 return type
| #
(?: string | record ) \s* \( \s* ([0-9]+) \s* \) (?: \s* (\*) | ) # fixed record, fixed string $3, ponter $4
| #
record \s* \( ( \s* (?: [A-Za-z_] [A-Za-z_0-9]* :: )* [A-Za-z_] [A-Za-z_0-9]* ) \s* \) (?: \s* (\*) | ) # record class $5, pointer $6
| #
( (?: [A-Za-z_] [A-Za-z_0-9]* \s+ )* [A-Za-z_] [A-Za-z_0-9]* ) \s* # unit type name $7
#
(?: (\*) | \[ ([0-9]*) \] | ) # pointer $8, array $9
| #
object \s* \( \s* ( (?: [A-Za-z_] [A-Za-z_0-9]* :: )* [A-Za-z_] [A-Za-z_0-9]* ) # object class $10
(?: \s*,\s* ( (?: [A-Za-z_] [A-Za-z_0-9]* \s+ )* [A-Za-z_] [A-Za-z_0-9]* ) )? # type $11
\s* \) #
) #
#
\s* # trailing white space
#
$/x; #
sub parse
{
my($self, $name, $opt) = @_;
$opt ||= {};
return $self->types->{$name} if $self->types->{$name};
$name =~ type_regex or croak "bad type name: $name";
if(defined (my $at = $1)) # closure
{
my $rt = $2;
return $self->types->{$name} = $self->create_type_closure(
$self->parse($rt, $opt),
map { $self->parse($_, $opt) } map { my $t = $_; $t =~ s/^\s+//; $t =~ s/\s+$//; $t } split /,/, $at,
);
}
if(defined (my $size = $3)) # fixed record / fixed string
{
croak "fixed record / fixed string size must be larger than 0"
unless $size > 0;
if(my $pointer = $4)
{
return $self->types->{$name} = $self->create_type_record(
0,
$size,
);
}
elsif($opt->{member})
{
return $self->types->{"$name *"} = $self->create_type_record(
0,
$size,
);
}
else
{
croak "fixed string / classless record not allowed as value type";
}
}
if(defined (my $class = $5)) # class record
{
my $size_method = $class->can('ffi_record_size') || $class->can('_ffi_record_size') || croak "$class has no ffi_record_size or _ffi_record_size method";
if(my $pointer = $6)
{
return $self->types->{$name} = $self->create_type_record(
0,
$class->$size_method,
$class,
);
}
else
{
return $self->types->{$name} = $self->create_type_record(
1,
$class->$size_method,
$class,
$class->_ffi_meta->ffi_type,
);
}
}
if(defined (my $unit_name = $7)) # basic type
{
if($self->global_types->{basic}->{$unit_name})
{
if(my $pointer = $8)
{
croak "void pointer not allowed" if $unit_name eq 'void';
return $self->types->{$name} = $self->global_types->{ptr}->{$unit_name};
}
if(defined (my $size = $9)) # array
{
croak "void array not allowed" if $unit_name eq 'void';
if($size ne '')
{
croak "array size must be larger than 0" if $size < 1;
return $self->types->{$name} = $self->create_type_array(
$self->global_types->{basic}->{$unit_name}->type_code,
$size,
);
}
else
{
return $self->global_types->{array}->{$unit_name} ||= $self->create_type_array(
$self->global_types->{basic}->{$unit_name}->type_code,
0,
);
}
}
# basic type with no decorations
return $self->global_types->{basic}->{$unit_name};
}
if(my $map_name = $self->type_map->{$unit_name})
{
if(my $pointer = $8)
{
return $self->types->{$name} = $self->parse("$map_name *", $opt);
}
if(defined (my $size = $9))
{
if($size ne '')
{
croak "array size must be larger than 0" if $size < 1;
return $self->types->{$name} = $self->parse("$map_name [$size]", $opt);
}
else
{
return $self->types->{$name} = $self->parse("$map_name []", $opt);
}
}
return $self->types->{$name} = $self->parse("$map_name", $opt);
}
if(my $pointer = $8)
{
my $unit_type = $self->parse($unit_name, $opt);
if($unit_type->is_record_value)
{
my $meta = $unit_type->meta;
return $self->types->{$name} = $self->create_type_record(
0,
$meta->{size},
$meta->{class},
);
}
my $basic_name = $self->global_types->{rev}->{$unit_type->type_code};
if($basic_name)
{
return $self->types->{$name} = $self->parse("$basic_name *", $opt);
}
else
{
croak "cannot make a pointer to $unit_name";
}
}
if(defined (my $size = $9))
{
my $unit_type = $self->parse($unit_name, $opt);
my $basic_name = $self->global_types->{rev}->{$unit_type->type_code};
if($basic_name)
{
if($size ne '')
{
croak "array size must be larger than 0" if $size < 1;
return $self->types->{$name} = $self->parse("$basic_name [$size]", $opt);
}
else
{
return $self->types->{$name} = $self->parse("$basic_name []", $opt);
}
}
else
{
croak "cannot make an array of $unit_name";
}
}
if($name eq 'string ro')
{
return $self->global_types->{basic}->{string};
}
elsif($name eq 'string rw')
{
return $self->global_types->{v2}->{string_rw} ||= $self->create_type_string(1);
}
return $self->types->{$name} || croak "unknown type: $unit_name";
}
if(defined (my $class = $10)) # object type
{
my $basic_name = $11 || 'opaque';
my $basic_type = $self->parse($basic_name);
if($basic_type->is_object_ok)
{
return $self->types->{$name} = $self->create_type_object(
$basic_type->type_code,
$class,
);
}
else
{
croak "cannot make an object of $basic_name";
}
}
croak "internal error parsing: $name";
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
FFI::Platypus::TypeParser::Version1 - FFI Type Parser Version One
=head1 VERSION
version 1.34
=head1 SYNOPSIS
use FFI::Platypus;
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->type('record(Foo::Bar)' => 'foo_bar_t');
$ffi->type('record(Foo::Bar)*' => 'foo_bar_ptr');
$ffi->type('opaque' => 'baz_t');
$ffi->type('bar_t*' => 'baz_ptr');
=head1 DESCRIPTION
This documents the second (version 1) type parser for L<FFI::Platypus>.
This type parser was included with L<FFI::Platypus> starting with version
C<0.91> in an experimental capability, and C<1.00> as a stable interface.
Starting with version C<1.00> the main L<FFI::Platypus> documentation
describes the version 1 API and you can refer to
L<FFI::Platypus::TypeParser::Version0> for details on the version0 API.
=head1 SEE ALSO
=over 4
=item L<FFI::Platypus>
The core L<FFI::Platypus> documentation.
=item L<FFI::Platypus::TypeParser::Version0>
The API C<0.02> type parser.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
Contributors:
Bakkiaraj Murugesan (bakkiaraj)
Dylan Cali (calid)
pipcet
Zaki Mughal (zmughal)
Fitz Elliott (felliott)
Vickenty Fesunov (vyf)
Gregor Herrmann (gregoa)
Shlomi Fish (shlomif)
Damyan Ivanov
Ilya Pavlov (Ilya33)
Petr Pisar (ppisar)
Mohammad S Anwar (MANWAR)
Håkon Hægland (hakonhagland, HAKONH)
Meredith (merrilymeredith, MHOWARD)
Diab Jerius (DJERIUS)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015,2016,2017,2018,2019,2020 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut