view release on metacpan or search on metacpan
Plugging leaks...
### Fixed
- Use `SAVEVPTR` and `SAVEDESTRUCTOR_X` to swap out arenas to fix leaky allocator in situations where tons of structs are passed in a list and need to be marshalled in only one direction
- Casting or binding an aggregate (`Affix::cast`, member pins) no longer leaks: member pins borrowed the freshly created parent hash/array as their lifeline, forming a strong reference cycle that Perl's refcounting cannot collect, so the whole pin tr...
- Passing a union to a wrapped call no longer segfaults: the argument sync read back *every* union member, and reading an inactive pointer/string member dereferenced the active member's float bytes as a C string pointer. Deep writes now skip members ...
- The library probe in `Affix::Platform::Unix` (`_findLib_gcc`) no longer prints linker errors (`undefined reference to WinMain`/`main`) while searching: it probes with `-shared`, which needs no entry point.
- Bitfields inside `Struct[...]` are no longer read or written out of bounds: `member->offset` now points at the storage unit base (with `bit_offset` relative to the unit) instead of the bitfield's own byte, so the unit-sized load/store in `push_stru...
- Reading and writing packed struct members (and pinned primitives) no longer uses unaligned native loads/stores: the dispatch vtables, bitfield vtables, pull handlers, and push handlers now round-trip through `memcpy`, which is safe on strict-alignm...
- Passing a wide string (`WString()`, i.e. `*wchar_t`) to a wrapped function now works on all platforms instead of croaking `Don't know how to handle this type of scalar as a pointer argument yet` on non-Windows systems, where the wide-string push op...
- Returning a `WString` no longer crashes: the wide-string pull handler called `SvGROW` on an uninitialized target SV, faulting before any buffer was allocated.
- [infix] Passing a 5-7 byte `Struct[...]` by value to a wrapped function no longer drops the trailing members on ARM64. The forward trampoline emitted a 32-bit register load unless the struct was exactly 8 bytes, so a `Struct[ arr => Array[2, UInt16...
## [v1.2.3] - 2026-08-08
### Fixed
- `Struct[...]` fields of type `WString` can now be assigned to (or as?) Perl strings (`SvPV`) rather than falling through and becoming null pointers
## [v1.2.2] - 2026-08-05
builder/Affix/Builder.pm view on Meta::CPAN
int main(void) { shm_open("/test", O_RDONLY, 0); return 0; }
END_C
my ( $fh, $src ) = tempfile( SUFFIX => '.c', UNLINK => 1 );
print $fh $test_code;
close $fh;
my ( $ofh, $out ) = tempfile( UNLINK => 1 );
close $ofh;
# Try without -lrt (list-form system to avoid shell injection)
open( my $devnull, '>', '/dev/null' ) if $^O ne 'MSWin32';
my $old_stdout = select $devnull if $devnull;
system( $cc, '-o', $out, $src );
select $old_stdout if $old_stdout;
close $devnull if $devnull;
return '' if $? == 0;
# Try with -lrt
open( $devnull, '>', '/dev/null' ) if $^O ne 'MSWin32';
$old_stdout = select $devnull if $devnull;
system( $cc, '-o', $out, $src, '-lrt' );
select $old_stdout if $old_stdout;
close $devnull if $devnull;
return '-lrt' if $? == 0;
return '';
}
sub command_exists {
my ($cmd) = @_;
if ( $Config{osname} eq 'MSWin32' ) {
return system( 'where', $cmd ) == 0;
}
infix/src/arch/aarch64/abi_arm64_emitters.c view on Meta::CPAN
INFIX_INTERNAL void emit_arm64_arith_imm(
code_buffer * buf, bool is_sub, bool is64, bool set_flags, arm64_gpr dest, arm64_gpr base, uint32_t imm) {
uint32_t instr = is_sub ? 0x51000000 : 0x11000000;
if (is64)
instr |= (1u << 31);
if (set_flags)
instr |= (1u << 29);
if (imm <= 0xFFF) // Check for un-shifted 12-bit immediate.
instr |= (imm & 0xFFF) << 10;
else if ((imm & 0xFFF) == 0 && (imm >> 12) <= 0xFFF && (imm >> 12) > 0) { // Check for shifted 12-bit immediate.
instr |= (1u << 22); // 'sh' bit selects LSL #12 shift.
instr |= ((imm >> 12) & 0xFFF) << 10;
}
else {
// Immediate is too large. Load it into a scratch register (X15) and do a register-based operation.
arm64_gpr scratch_reg = X15_REG;
emit_arm64_load_u64_immediate(buf, scratch_reg, imm);
uint32_t reg_instr = is_sub ? 0x4B000000 : 0x0B000000;
if (is64)
reg_instr |= (1u << 31);
if (set_flags)
infix/src/arch/riscv/abi_riscv64_common.h view on Meta::CPAN
// funct7 fields (bits 31:25) for R-type integer ops
#define RV_F7_ADD 0x00U
#define RV_F7_SUB 0x20U
#define RV_F7_SLL 0x00U
#define RV_F7_SRL 0x00U
#define RV_F7_SRA 0x20U
// Shift immediate funct6 (bits 31:26)
#define RV_F6_SLLI 0x00U
#define RV_F6_SRLI 0x00U
#define RV_F6_SRAI 0x10U
// Floating-point move instructions (funct7 selects the operation).
// The RV64 W-level moves transfer between GPRs and single-precision FP registers.
#define RV_F7_FMV_X_S 0x70U // FMV.X.W: FP(single) -> GPR
#define RV_F7_FMV_S_X 0x78U // FMV.W.X: GPR -> FP(single)
#define RV_F7_FMV_X_D 0x71U // FMV.X.D: FP(double) -> GPR
#define RV_F7_FMV_D_X 0x79U // FMV.D.X: GPR -> FP(double)
#define RV_F7_FMV_X_H 0x72U // FMV.X.H (Zfh)
#define RV_F7_FMV_H_X 0x7AU // FMV.H.X (Zfh)
// FP-to-FP moves are the FSGNJ pseudo-instructions (fmv.s rd,rs1 == fsgnj.s rd,rs1,rs1).
#define RV_F7_FSGNJ_S 0x10U // FSGNJ.S
#define RV_F7_FSGNJ_D 0x11U // FSGNJ.D
// The 'imm' (rs2) encodings used by the fcvt family to select source/target precision.
// The source precision selects funct7 bit 0: 0 = 32-bit source (FCVT.S.D), 1 = 64-bit source (FCVT.D.S).
#define RV_FCVT_RM_RNE 0x0U // Round to nearest, ties to even
#define RV_F7_FCVT_S_D 0x20U // FCVT.S.D: source is double-precision, dest is single
#define RV_F7_FCVT_D_S 0x21U // FCVT.D.S: source is single-precision, dest is double
#define RV_RS2_FCVT_D_S 0x00U // rs2 field for FCVT.D.S
#define RV_RS2_FCVT_S_D 0x01U // rs2 field for FCVT.S.D
// Instruction bit-field helper macros
#define RV_RS1_SHIFT 15U
#define RV_RS2_SHIFT 20U
#define RV_RD_SHIFT 7U
#define RV_FUNCT3_SHIFT 12U
infix/src/arch/x64/abi_x64_emitters.c view on Meta::CPAN
bool b,
uint8_t aaa) // Masking/control bits
{
emit_byte(buf, 0x62);
// Byte 2: P0 - R, X, B, R' bits are inverted. 0 means 1, 1 means 0.
uint8_t p0 = 0;
p0 |= (R ? 0 : 1) << 7; // Inverted R bit
p0 |= (X ? 0 : 1) << 6; // Inverted X bit
p0 |= (B ? 0 : 1) << 5; // Inverted B bit
p0 |= (R_prime ? 0 : 1) << 4; // Inverted R' bit
p0 |= (map & 0x0F); // Low 4 bits select the opcode map (0F, 0F38, 0F3A)
emit_byte(buf, p0);
// Byte 3: P1
uint8_t p1 = 0;
p1 |= (pp & 0b11);
p1 |= (1 << 2); // ' (marks EVEX), must be 1
p1 |= ((~vvvv & 0xF) << 3); // vvvv field is inverted
p1 |= W ? (1 << 7) : 0;
emit_byte(buf, p1);
// Byte 4: P2
uint8_t p2 = 0;
infix/src/common/infix_config.h view on Meta::CPAN
/**
* @file infix_config.h
* @brief Platform, architecture, and ABI detection macros.
* @ingroup internal_common
*
* @details This header is the first to be included by `infix_internals.h` and is
* responsible for defining a consistent set of `INFIX_*` macros that describe the
* build environment. It is the central point of configuration for the entire library,
* adapting the build to different operating systems, compilers, and CPU architectures.
*
* Its most critical function is to select the correct **Application Binary Interface (ABI)**
* implementation to use for JIT code generation. This is achieved through a cascade
* of preprocessor checks that can be overridden by the user for cross-compilation.
* By the end of this file, exactly one `INFIX_ABI_*` macro must be defined, which
* determines which `abi_*.c` file is included in the unity build.
*
* @internal
*/
#pragma once
// System Feature Test Macros
/**
infix/src/jit/trampoline.c view on Meta::CPAN
extern const infix_direct_forward_abi_spec g_arm64_direct_forward_spec;
#elif defined(INFIX_ABI_RISCV64)
extern const infix_forward_abi_spec g_riscv64_forward_spec;
extern const infix_reverse_abi_spec g_riscv64_reverse_spec;
extern const infix_direct_forward_abi_spec g_riscv64_direct_forward_spec;
#endif
/**
* @internal
* @brief Retrieves a pointer to the ABI specification v-table for forward calls.
* @details This function is the entry point to the ABI abstraction layer. It uses
* compile-time preprocessor macros (defined in `infix_config.h`) to select and
* return the correct v-table for the target platform.
* @return A pointer to the active `infix_forward_abi_spec`, or `nullptr` if the
* platform is unsupported.
*/
const infix_forward_abi_spec * get_current_forward_abi_spec() {
#if defined(INFIX_ABI_WINDOWS_X64)
return &g_win_x64_forward_spec;
#elif defined(INFIX_ABI_SYSV_X64)
return &g_sysv_x64_forward_spec;
#elif defined(INFIX_ABI_AAPCS64)
infix/src/jit/trampoline.c view on Meta::CPAN
#elif defined(INFIX_ABI_SYSV_X64)
#include "../arch/x64/abi_sysv_x64.c"
#include "../arch/x64/abi_x64_emitters.c"
#elif defined(INFIX_ABI_AAPCS64)
#include "../arch/aarch64/abi_arm64.c"
#include "../arch/aarch64/abi_arm64_emitters.c"
#elif defined(INFIX_ABI_RISCV64)
#include "../arch/riscv/abi_riscv64.c"
#include "../arch/riscv/abi_riscv64_emitters.c"
#else
#error "No supported ABI was selected for the unity build in trampoline.c."
#endif
lib/Affix/Wrap.pod view on Meta::CPAN
Optional. An array reference of paths to search for C<#include "..."> directives. The directory of every file listed in
C<project_files> is automatically added to this list.
=item C<types>
Optional. A hash reference for manually mapping type names to L<Affix> type objects or definition strings. Very useful
for masking complex internal library structures behind Pointer[Void] handles.
=item C<driver>
Optional. Explicitly select the parser driver. Values are C<'Clang'> or C<'Regex'>. If omitted, C<Affix::Wrap> attempts
to find the C<clang> executable and falls back to Regex if unavailable.
=back
=head1 METHODS
Parse headers, inject bindings at runtime, or generate standalone .pm files:
=head2 wrap( $lib, [$target_package] )
t/019_fileio.t view on Meta::CPAN
affix $lib, 'c_create_tmpfile', [] => Pointer [File];
affix $lib, 'c_is_null_file', [ Pointer [File] ] => Int;
#
subtest 'Writing to a Perl filehandle from C' => sub {
my $file = tempfile( { realpath => 1 } );
my $fh = $file->filehandle('>');
# Note: We use a real file because PerlIO_findFILE (used internally)
# requires a valid C-level FILE* which scalar handles (\$) might not provide.
# Turn off buffering to ensure C sees the file state immediately
my $old_fh = select($fh);
$| = 1;
select($old_fh);
my $bytes = c_write_to_file( $fh, 'Hello from C' );
ok $bytes > 0, 'C function returned success count';
close $fh;
# Verify content
my $check = $file->filehandle('<');
my $content = <$check>;
is $content, 'Hello from C', 'Data written by C appears in file';
};
subtest 'Reading from a Perl filehandle in C' => sub {
t/019_fileio.t view on Meta::CPAN
typedef Logger => Struct [ log_file => Pointer [File], counter => Int ];
# Bind functions
affix $lib, 'init_logger', [ Pointer [ Logger() ], Pointer [File] ] => Void;
affix $lib, 'log_message', [ Pointer [ Logger() ], String ] => Void;
affix $lib, 'create_logger', [ Pointer [File] ] => Logger();
subtest 'File inside Struct (Pointer)' => sub {
my $file = tempfile( { realpath => 1 } );
my $filename = $file->stringify;
my $fh = $file->filehandle('+>');
my $old_fh = select($fh);
$| = 1;
select($old_fh);
# Allocate struct memory
my $logger = malloc( sizeof( Logger() ) );
# Pass filehandle to C to store in struct
init_logger( $logger, $fh );
# Verify via C function
log_message( $logger, 'First message' );
log_message( $logger, 'Second message' );
t/019_fileio.t view on Meta::CPAN
like $lines[0], qr/\[1\] First message/, 'Line 1 matches';
like $lines[1], qr/\[2\] Second message/, 'Line 2 matches';
# Keep $fh alive until test end to avoid closing underneath C
close $fh;
};
subtest 'File inside Struct (Value Return)' => sub {
my $file = tempfile( { realpath => 1 } );
my $filename = $file->stringify;
my $fh = $file->filehandle('+>');
my $old_fh = select($fh);
$| = 1;
select($old_fh);
# Call C function returning a struct by value
my $logger_hash = create_logger($fh);
is $logger_hash->{counter}, 100, 'Counter is correct';
ok $logger_hash->{log_file}, 'Got filehandle back';
is ref( $logger_hash->{log_file} ), 'GLOB', 'It is a glob';
# Write using the returned handle to verify it works
# Note: $logger_hash->{log_file} wraps the same FILE* as $fh.
ok syswrite( $logger_hash->{log_file}, "Direct write from Perl" ), 'syswrite to the handle from Perl';
t/019_fileio.t view on Meta::CPAN
my $f1 = $file1->stringify;
my $fh1 = $file1->filehandle('+>');
my $file2 = tempfile( { realpath => 1 } );
my $f2 = $file2->stringify;
my $fh2 = $file2->filehandle('+>');
my $file3 = tempfile( { realpath => 1 } );
my $f3 = $file3->stringify;
my $fh3 = $file3->filehandle('+>');
# Flush buffers
for my $h ( $fh1, $fh2, $fh3 ) { my $o = select($h); $| = 1; select($o); }
# Pass array of handles
write_all( [ $fh1, $fh2, $fh3 ], 'Broadcast' );
close $_ for ( $fh1, $fh2, $fh3 );
# Verify
for my $f ( $f1, $f2, $f3 ) {
open my $in, '<', $f;
is <$in>, 'Broadcast', "File $f written to";
close $in;