通过 FFI::Platypus 在 Perl 中获取缓冲区大小
Getting size of buffer in Perl via FFI::Platypus
我正在使用 FFI::Platypus 调用 C 函数。该函数有四个参数:两个字符串、一个指向缓冲区的指针和缓冲区大小:
int helper_getAddr(
const char *firstName,
const char *lastName,
char **address,
size_t *addressLen);
我是这样定义的 FFI::Platypus:
$self->{ffi}->attach( [helper_getAddr => 'get_addr']
=> ['string', 'string', 'string', 'size_t'] => 'int' );
然后像这样调用:
my $contents_ptr = malloc 100;
my $size;
my $success = get_addr( "Tom", "Baker", $contents_ptr, $size );
所以我可以使用 buffer_to_scalar
来获取缓冲区内容。
调用似乎有效 - $success = 0
和 $contents_ptr
已定义 - 但 $size
未设置 。
我很少使用 C/C++,所以我不确定我的问题是在 $size
的声明中,还是我需要更改对 get_addr()
.
或者,在我调用 buffer_to_scalar
之前,有没有一种方法可以直接在 Perl 中使用 $contents_ptr
来查找缓冲区的长度?
以下是错误的:
[ 'string', 'string', 'string', 'size_t' ]
helper_getAddr
的第三个参数不是字符串。
helper_getAddr
的第四个参数不是 size_t
。
在继续之前,我们必须确定函数需要什么。这是有问题的,因为函数的参数没有多大意义。
如果 helper_getAddr
填充现有缓冲区,我希望
int helper_getAddr(
const char *firstName,
const char *lastName,
char **address,
size_t *addressLen
);
如果 helper_getAddr
分配并 returns 一个字符串,我希望
char *helper_getAddr(
const char *firstName,
const char *lastName
);
或
int helper_getAddr(
const char *firstName,
const char *lastName,
char **address
);
除了有一个额外的无用参数外,helper_getAddr
最类似于分配和 returns 字符串的函数。所以我将在 helper_getAddr
分配和 returns 一个字符串的假设下继续。
鉴于我们拥有的功能,我们很想使用以下内容:
$ffi->attach(
[ 'helper_getAddr' => '_get_addr' ],
[ 'string', 'string', 'string*', 'size_t*' ], # XXX
'int',
);
问题是它不允许我们访问我们需要释放的指针。因此,我们将使用
$ffi->attach(
[ 'helper_getAddr' => '_get_addr' ],
[ 'string', 'string', 'opaque*', 'size_t*' ],
'int',
);
一个简单的包装器将为函数提供 Perl-ish 外观并处理释放缓冲区。
sub get_addr {
_get_addr($_[0], $_[1], \my $buf, \my $buf_size)
or return undef;
my $addr = $ffi->cast('opaque' => 'string', $buf);
free($buf);
return $addr;
}
- 我们不在 Perl 中调用
malloc
,因为该函数会简单地覆盖指针,从而导致内存泄漏。
- 通过将缓冲区转换为
string
类型,Platypus::FFI 将从返回缓冲区的 NUL-terminated 内容创建一个 Perl 字符串,因此不需要 buffer_to_scalar
要么。
用法示例:
say get_addr("Tom", "Baker") // "[undef]";
完整的解决方案如下。
lookup
#!/home/ikegami/usr/perlbrew/perls/5.32.0t/bin/perl
use strict;
use warnings;
use feature qw( say state );
use FindBin qw( $RealBin );
use FFI::Platypus qw( );
use FFI::Platypus::Memory qw( free );
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->find_lib(
lib => 'lookup',
libpath => $RealBin,
);
$ffi->attach(
[ 'get_addr' => '_get_addr' ],
[ 'string', 'string', 'opaque*', 'size_t*' ],
'int',
);
sub get_addr {
_get_addr($_[0], $_[1], \my $buf, \my $buf_size)
or return undef;
my $addr = $ffi->cast('opaque' => 'string', $buf);
free($buf);
return $addr;
}
say get_addr("Tom", "Baker") // "[undef]";
liblookup.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int get_addr(
const char *first_name,
const char *last_name,
char **address_ptr,
size_t *address_size_ptr
) {
const char *prefix = "Address of ";
const size_t len_prefix = strlen(prefix);
const size_t len_f_n = strlen(first_name);
const size_t len_l_n = strlen(last_name);
*address_size_ptr = len_prefix + len_f_n + 1 + len_l_n + 1;
*address_ptr = malloc(*address_size_ptr);
if (!*address_ptr)
return 0;
char *p = *address_ptr;
memmove(p, prefix, len_prefix); p += len_prefix;
memmove(p, first_name, len_f_n); p += len_f_n;
*p = ' '; ++p;
memmove(p, last_name, len_l_n); p += len_l_n;
*p = 0; ++p;
return 1;
}
run
#!/bin/bash
trap 'printf '\''error!\n'\''; exit 1' ERR
script="$( readlink -e -- "[=20=]" )"
script_dir="$( dirname -- "$script" )"
home_dir="$script_dir"
cd "$home_dir"
prog=./lookup
# Use the PATH to locate the program.
prog="$( which -- "$prog" )"
# Use the program's shebang to locate the appropriate perl.
perl="$( perl -ne'chomp; print s/^#!//r; exit;' "$prog" )"
# Extract compiler and linker information from the correct perl.
get_config() { "$perl" -MConfig -e'print $Config{$ARGV[0]}' ""; }
cc="$( get_config cc )"
ccflags="$( get_config ccflags )"
optimize="$( get_config optimize )"
cccdlflags="$( get_config cccdlflags )"
ld="$( get_config ld )"
lddlflags="$( get_config lddlflags )"
# Build the shared library.
"$cc" -c $ccflags $optimize $cccdlflags liblookup.c -o liblookup.o
"$ld" $lddlflags liblookup.o -o liblookup.so
# Run our test.
"$prog"
我正在使用 FFI::Platypus 调用 C 函数。该函数有四个参数:两个字符串、一个指向缓冲区的指针和缓冲区大小:
int helper_getAddr(
const char *firstName,
const char *lastName,
char **address,
size_t *addressLen);
我是这样定义的 FFI::Platypus:
$self->{ffi}->attach( [helper_getAddr => 'get_addr']
=> ['string', 'string', 'string', 'size_t'] => 'int' );
然后像这样调用:
my $contents_ptr = malloc 100;
my $size;
my $success = get_addr( "Tom", "Baker", $contents_ptr, $size );
所以我可以使用 buffer_to_scalar
来获取缓冲区内容。
调用似乎有效 - $success = 0
和 $contents_ptr
已定义 - 但 $size
未设置 。
我很少使用 C/C++,所以我不确定我的问题是在 $size
的声明中,还是我需要更改对 get_addr()
.
或者,在我调用 buffer_to_scalar
之前,有没有一种方法可以直接在 Perl 中使用 $contents_ptr
来查找缓冲区的长度?
以下是错误的:
[ 'string', 'string', 'string', 'size_t' ]
helper_getAddr
的第三个参数不是字符串。helper_getAddr
的第四个参数不是size_t
。
在继续之前,我们必须确定函数需要什么。这是有问题的,因为函数的参数没有多大意义。
如果 helper_getAddr
填充现有缓冲区,我希望
int helper_getAddr(
const char *firstName,
const char *lastName,
char **address,
size_t *addressLen
);
如果 helper_getAddr
分配并 returns 一个字符串,我希望
char *helper_getAddr(
const char *firstName,
const char *lastName
);
或
int helper_getAddr(
const char *firstName,
const char *lastName,
char **address
);
除了有一个额外的无用参数外,helper_getAddr
最类似于分配和 returns 字符串的函数。所以我将在 helper_getAddr
分配和 returns 一个字符串的假设下继续。
鉴于我们拥有的功能,我们很想使用以下内容:
$ffi->attach(
[ 'helper_getAddr' => '_get_addr' ],
[ 'string', 'string', 'string*', 'size_t*' ], # XXX
'int',
);
问题是它不允许我们访问我们需要释放的指针。因此,我们将使用
$ffi->attach(
[ 'helper_getAddr' => '_get_addr' ],
[ 'string', 'string', 'opaque*', 'size_t*' ],
'int',
);
一个简单的包装器将为函数提供 Perl-ish 外观并处理释放缓冲区。
sub get_addr {
_get_addr($_[0], $_[1], \my $buf, \my $buf_size)
or return undef;
my $addr = $ffi->cast('opaque' => 'string', $buf);
free($buf);
return $addr;
}
- 我们不在 Perl 中调用
malloc
,因为该函数会简单地覆盖指针,从而导致内存泄漏。 - 通过将缓冲区转换为
string
类型,Platypus::FFI 将从返回缓冲区的 NUL-terminated 内容创建一个 Perl 字符串,因此不需要buffer_to_scalar
要么。
用法示例:
say get_addr("Tom", "Baker") // "[undef]";
完整的解决方案如下。
lookup
#!/home/ikegami/usr/perlbrew/perls/5.32.0t/bin/perl
use strict;
use warnings;
use feature qw( say state );
use FindBin qw( $RealBin );
use FFI::Platypus qw( );
use FFI::Platypus::Memory qw( free );
my $ffi = FFI::Platypus->new( api => 1 );
$ffi->find_lib(
lib => 'lookup',
libpath => $RealBin,
);
$ffi->attach(
[ 'get_addr' => '_get_addr' ],
[ 'string', 'string', 'opaque*', 'size_t*' ],
'int',
);
sub get_addr {
_get_addr($_[0], $_[1], \my $buf, \my $buf_size)
or return undef;
my $addr = $ffi->cast('opaque' => 'string', $buf);
free($buf);
return $addr;
}
say get_addr("Tom", "Baker") // "[undef]";
liblookup.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int get_addr(
const char *first_name,
const char *last_name,
char **address_ptr,
size_t *address_size_ptr
) {
const char *prefix = "Address of ";
const size_t len_prefix = strlen(prefix);
const size_t len_f_n = strlen(first_name);
const size_t len_l_n = strlen(last_name);
*address_size_ptr = len_prefix + len_f_n + 1 + len_l_n + 1;
*address_ptr = malloc(*address_size_ptr);
if (!*address_ptr)
return 0;
char *p = *address_ptr;
memmove(p, prefix, len_prefix); p += len_prefix;
memmove(p, first_name, len_f_n); p += len_f_n;
*p = ' '; ++p;
memmove(p, last_name, len_l_n); p += len_l_n;
*p = 0; ++p;
return 1;
}
run
#!/bin/bash
trap 'printf '\''error!\n'\''; exit 1' ERR
script="$( readlink -e -- "[=20=]" )"
script_dir="$( dirname -- "$script" )"
home_dir="$script_dir"
cd "$home_dir"
prog=./lookup
# Use the PATH to locate the program.
prog="$( which -- "$prog" )"
# Use the program's shebang to locate the appropriate perl.
perl="$( perl -ne'chomp; print s/^#!//r; exit;' "$prog" )"
# Extract compiler and linker information from the correct perl.
get_config() { "$perl" -MConfig -e'print $Config{$ARGV[0]}' ""; }
cc="$( get_config cc )"
ccflags="$( get_config ccflags )"
optimize="$( get_config optimize )"
cccdlflags="$( get_config cccdlflags )"
ld="$( get_config ld )"
lddlflags="$( get_config lddlflags )"
# Build the shared library.
"$cc" -c $ccflags $optimize $cccdlflags liblookup.c -o liblookup.o
"$ld" $lddlflags liblookup.o -o liblookup.so
# Run our test.
"$prog"