view release on metacpan or search on metacpan
- SIMD Vector Improvements:
- Added M512, M512d, and M512i type helpers.
- Ensured compatibility with infix's refined vector alignment and passing rules.
- [infix] Added support for half-precision floating-point (`float16`).
- [infix] Implemented C++ exception propagation through JIT frames on Linux (x86-64 and ARM64) using manual DWARF `.eh_frame` generation and `__register_frame`.
- [infix] Implemented Structured Exception Handling (SEH) for Windows x64 and ARM64 for C++ exception propagation through trampolines.
- [infix] Added `infix_forward_create_safe` API to establish an exception boundary that catches native exceptions and returns a dedicated error code (`INFIX_CODE_NATIVE_EXCEPTION`).
- [infix] Added support for 256-bit (AVX) and 512-bit (AVX-512) vectors in the System V ABI.
- [infix] Added support for receiving bitfield structs in reverse call trampolines.
- [infix] Added trampoline caching. Identical signatures and targets now share the same JIT-compiled code and metadata via internal reference counting, significantly reducing memory overhead and initialization time.
- [infix] Added a new opt-in build mode (`--sanity`) that emits extra JIT instructions to verify stack pointer consistency around user-provided marshaller calls, making it easier to debug corrupting language bindings.
### Changed
- Pull infix v0.1.6.
- [infix] Explicitly enabled 16-byte stack alignment in Windows x64 trampolines to ensure SIMD compatibility.
- [infix] Updated `infix_type_create_vector` to use the vector's full size for its natural alignment (e.g., 32-byte alignment for `__m256`).
- [infix] Refined the Windows x64 ABI to pass all vector types by reference (pointer in GPR). This ensures compatibility with MSVC which expects even 128-bit vectors to be passed via pointer in many scenarios, while still returning them by value in `...
- [infix] Move to a pre-calculated hash field in `_infix_registry_entry_t`. Lookups and rehashing now use this stored hash, significantly reducing string hashing overhead during type resolution and registry scaling.
- [infix] Optimized Type Registry memory management: Internal hash table buckets are now heap-allocated and freed during rehashes, preventing memory "leaks" within the registry's arena.
```
**Note:** You must call `errno()` immediately after the C function invokes, as subsequent Perl operations (like
printing to STDOUT) might overwrite the system's error register.
## Memory Inspection
### `dump( $pin, $length_in_bytes )`
Prints a formatted hex dump of the memory pointed to by a Pin directly to `STDOUT`. This is an invaluable tool for
verifying that C structs or buffers contain the data you expect.
```perl
my $ptr = strdup("Affix Debugging");
dump($ptr, 16);
# Output:
# Dumping 16 bytes from 0x55E9A8A5 at script.pl line 42
# 000 41 66 66 69 78 20 44 65 62 75 67 67 69 6e 67 00 | Affix Debugging.
```
infix/include/infix/infix.h view on Meta::CPAN
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Retrieves the version of the infix library linked at runtime.
*
* @details This function allows applications to verify that the version of the
* library they are linked against matches the headers they were compiled with.
* This is particularly useful when loading `infix` as a shared library/DLL
* to detect version mismatches.
*
* @return An `infix_version_t` structure containing the major, minor, and patch numbers.
*/
INFIX_API INFIX_NODISCARD infix_version_t infix_get_version(void);
/**
* @defgroup high_level_api High-Level Signature API
infix/src/arch/aarch64/abi_arm64_common.h view on Meta::CPAN
*
* 1. **Register Enumerations:** It defines enums for the general-purpose registers (GPRs) and
* the floating-point/SIMD registers (VPRs). These enums provide a clear, type-safe,
* and self-documenting way to refer to specific registers when emitting machine
* code or implementing the ABI logic. The comments on each register describe its
* role according to the standard AAPCS64 calling convention.
*
* 2. **Instruction Encoding Constants:** It contains preprocessor definitions for the
* fixed bitfields of various AArch64 instructions. This abstracts away the
* "magic numbers" of machine code generation, making the emitter code in
* `abi_arm64_emitters.c` more readable and easier to verify against the official
* ARM Architecture Reference Manual.
*
* By centralizing these definitions, this header provides a single source of truth for
* the low-level architectural details, separating them from the higher-level ABI logic.
* @endinternal
*/
#include <stdint.h>
/**
* @internal
* @enum arm64_gpr
infix/src/arch/aarch64/abi_arm64_common.h view on Meta::CPAN
A64_COND_LE = 0xD, ///< Less Than or Equal (Signed)
A64_COND_AL = 0xE, ///< Always
} arm64_cond;
/**
* @internal
* @defgroup aarch64_opcodes AArch64 Instruction Opcodes and Bitfields
* @brief Defines for the bit-level encoding of AArch64 instructions.
* @details These constants represent the fixed bit patterns for various instruction
* classes as specified in the ARM Architecture Reference Manual. Using these
* defines instead of raw hex literals makes the emitter code more readable
* and easier to verify. The `U` suffix is critical to prevent signed
* integer overflow during bit-shifting operations at compile time.
* @{
*/
// Common bitfields
#define A64_SF_64BIT (1U << 31) // 'sf' (size field) bit for 64-bit operations
#define A64_SF_32BIT (0U << 31) // 'sf' bit for 32-bit operations
#define A64_V_VECTOR (1U << 26) // Vector bit for SIMD/FP instructions
// Data Processing -- Immediate (e.g., ADD, SUB)
#define A64_OPC_ADD (0b00U << 29)
#define A64_OPC_ADDS (0b01U << 29)
infix/src/common/infix_config.h view on Meta::CPAN
* from a source (like the parser's temporary arena) into its own private arena.
* The size of the source arena is used as a hint for the new arena's size, but the
* copy process itself requires a small amount of extra memory for its own bookkeeping
* (e.g., the memoization list in `_copy_type_graph_to_arena_recursive`). This
* headroom provides that extra space to prevent allocation failures during the copy.
*/
#define INFIX_TRAMPOLINE_HEADROOM 128
/**
* @def INFIX_SANITY_CHECK_ENABLE
* @brief If defined and non-zero, the JIT will emit extra instructions to verify
* stack consistency around user-provided marshaller calls.
*/
#ifndef INFIX_SANITY_CHECK_ENABLE
#define INFIX_SANITY_CHECK_ENABLE 0
#endif
/**
* @def INFIX_INTERNAL
* @brief When compiling with -fvisibility=hidden, we use this to explicitly mark internal-but-shared functions as
* hidden.
infix/src/core/signature.c view on Meta::CPAN
* @details This module is responsible for two key functionalities that form the
* user-facing API of the library:
*
* 1. **Parsing:** It contains a hand-written recursive descent parser that transforms a
* human-readable signature string (e.g., `"({int, *char}) -> void"`) into an
* unresolved `infix_type` object graph. This is the **"Parse"** stage of the core
* data pipeline. The internal entry point for the "Parse" stage is `_infix_parse_type_internal`.
*
* 2. **Printing:** It provides functions to serialize a fully resolved `infix_type`
* graph back into a canonical signature string. This is crucial for introspection,
* debugging, and verifying the library's understanding of a type.
*
* The public functions `infix_type_from_signature` and `infix_signature_parse`
* are high-level orchestrators. They manage the entire **"Parse -> Copy -> Resolve -> Layout"**
* pipeline, providing the user with a fully validated, self-contained, and ready-to-use
* type object that is safe to use for the lifetime of its returned arena.
*/
#include "common/infix_internals.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
lib/Affix.pod view on Meta::CPAN
}
B<Note:> You must call C<errno()> immediately after the C function invokes, as subsequent Perl operations (like
printing to STDOUT) might overwrite the system's error register.
=head2 Memory Inspection
=head3 C<dump( $pin, $length_in_bytes )>
Prints a formatted hex dump of the memory pointed to by a Pin directly to C<STDOUT>. This is an invaluable tool for
verifying that C structs or buffers contain the data you expect.
my $ptr = strdup("Affix Debugging");
dump($ptr, 16);
# Output:
# Dumping 16 bytes from 0x55E9A8A5 at script.pl line 42
# 000 41 66 66 69 78 20 44 65 62 75 67 67 69 6e 67 00 | Affix Debugging.
=head3 C<sv_dump( $scalar )>
t/007_pointers.t view on Meta::CPAN
# Cast returns a new pin. We must assign it or use the returned object.
# Also, we keep $mem alive to ensure the memory isn't freed if $int_ptr assumes
# $mem owns it (though cast usually creates unmanaged aliases, so we need $mem to stay alive).
my $int_ptr = Affix::cast( $mem, Pointer [Int] );
# Test magical 'set' via dereferencing
# $$int_ptr is a scalar magic that writes to the address
$$int_ptr = 42;
# Use the original $mem pointer for reading (verifying they point to the same place)
is( read_int_from_void_ptr($mem), 42, 'Magical set via deref wrote to C memory' );
# Test cast again
my $long_ptr = Affix::cast( $mem, Pointer [LongLong] );
$$long_ptr = 1234567890123;
is $$long_ptr, 1234567890123, 'Magical get after casting to a new type works';
# Test realloc
my $r_ptr = calloc( 2, Int );
t/009_128bit.t view on Meta::CPAN
DLLEXPORT int has_int128() { return 1; }
DLLEXPORT int128 add_i128(int128 a, int128 b) {
return a + b;
}
DLLEXPORT uint128 add_u128(uint128 a, uint128 b) {
return a + b;
}
// Helper to verify value passed correctly (returns high 64 bits cast to 64)
DLLEXPORT int64_t high_bits_i128(int128 v) {
return (int64_t)(v >> 64);
}
#else
DLLEXPORT int has_int128() { return 0; }
#endif
END_C
# Compile the library
my $lib = compile_ok($c_source);
t/014_array.t view on Meta::CPAN
ok affix( $lib_path, 'sum_float_array', '(*float, int)->float' ), 'affix sum_float_array';
my $floats = [ 1.1, 2.2, 3.3 ];
is( sum_float_array( $floats, 3 ), float( 6.6, tolerance => 0.01 ), 'Correctly summed an array of floats' );
#
isa_ok my $sum_arr = wrap( $lib_path, 'sum_array_static', [ Array [ Int, 5 ] ] => Int ), ['Affix'];
is $sum_arr->( [ 1, 2, 3, 4, 5 ] ), 15, 'Fixed size array passed by value';
#
subtest 'Fixed Array Binary Safety' => sub {
my $ptr = calloc( 5, Int8 ); # Allocate 5 bytes
my $mem = cast( $ptr, Array [ Int8, 5 ] ); # Write: "A \0 B \0 C"
$$mem = [ 65, 0, 66, 0, 67 ]; # We can write using an array ref (slow path, verify write works)
my $view = cast( $ptr, Array [ Char, 5 ] ); # Now read it back as a char array (fast path)
my $data = $$view;
is length($data), 5, 'Binary string has correct length (5)';
is $data, "A\0B\0C", 'Binary content preserved (nulls included)';
free($ptr);
};
done_testing;
t/019_fileio.t view on Meta::CPAN
}
return fp;
}
// Identity function to test round-tripping a PerlIO pointer.
// Since we don't link against libperl here, we treat PerlIO* as void*.
DLLEXPORT void* c_perlio_identity(void* p) {
return p;
}
// Check if FILE* is NULL (to verify failure cases)
DLLEXPORT int c_is_null_file(FILE* fp) {
return fp == NULL;
}
END_C
subtest 'Standard C FILE* (Affix::File)' => sub {
# File represents the FILE struct, so Pointer[File] is FILE*
affix $lib, 'c_write_to_file', [ Pointer [File], String ] => Int;
affix $lib, 'c_read_char', [ Pointer [File] ] => Int;
affix $lib, 'c_create_tmpfile', [] => Pointer [File];
t/019_fileio.t view on Meta::CPAN
# 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' );
# Verify Perl side struct access
# Note: Pulling a File handle usually creates a new GLOB wrapper around the FILE*
# Since we own $fh, let's verify checking against undef works
my $logger_struct = cast( $logger, Logger() ); # View as struct
my $retrieved_fh = $logger_struct->{log_file};
ok $retrieved_fh, 'Retrieved filehandle from struct';
is ref($retrieved_fh), 'GLOB', 'It is a glob';
# Write from Perl using retrieved handle
# print {$retrieved_fh} "From Perl\n"; # Careful, might double-close if not careful
# Check file content
open my $check, '<', $filename;
my @lines = <$check>;
t/019_fileio.t view on Meta::CPAN
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\n" ), 'syswrite to the handle from Perl';
# To avoid double-close warnings, we let Perl handle cleanup of the glob
# but be careful about explicit closes.
undef $logger_hash;
# Check
open my $check, '<', $filename;
my $content = <$check>;
t/020_deep_types.t view on Meta::CPAN
ok $ptr, 'Got a pointer';
# Dereference to read (Deep copy from C -> Perl)
my $val = $$ptr;
is $val, { top_left => { x => 0, y => 0 }, bottom_right => { x => 10, y => 20 } }, 'Dereferenced nested struct pointer correctly';
# Modify via pointer using pinning syntax logic (write to C)
# Since $ptr is magic, assigning to $$ptr should marshal data back to C memory
$$ptr = { top_left => { x => 99, y => 99 }, bottom_right => { x => 100, y => 100 } };
# Read again to verify round-trip
my $val_new = $$ptr;
is $val_new->{top_left}{x}, 99, 'Write-back to static C struct via magic successful';
# Verify it persists (call the C accessor again)
my $ptr2 = $get_static_rect->();
is $$ptr2->{top_left}{x}, 99, 'Changes persisted in C memory';
};
#
done_testing;
t/023_sockaddr.t view on Meta::CPAN
#include "std.h"
//ext: .c
// Minimal definition to ensure struct layout matches system
#if defined(_WIN32)
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
// We implement a manual byte swap to verify the data arrived correctly
// without needing to link against system network libraries (ws2_32.dll etc)
// which simplifies the test build process.
DLLEXPORT int get_port_raw(struct sockaddr_in* sa) {
if (!sa) return -1;
// Return raw network-byte-order value
return sa->sin_port;
}
DLLEXPORT unsigned long get_addr(struct sockaddr_in* sa) {
if (!sa) return 0;
t/023_sockaddr.t view on Meta::CPAN
# Verify Port
# C returns raw network short (Big Endian).
# unpack('n') converts "Network to Native".
my $raw_port_from_c = get_port_raw($sa);
my $port_back = unpack 'n', pack 'S', $raw_port_from_c;
# On Little Endian systems (x86), pack('S') puts the bytes in LE.
# But wait, C returned a UShort (number).
# If C read 0x1F90 (8080) from memory as a short on LE, it saw 0x901F (36895).
# Let's just verify round-trip logic via 'n' (Network order).
# Simpler check: Just pack the Perl port into network order and compare values
my $expected_raw_port = unpack 'S', pack( 'n', $port );
is $raw_port_from_c, $expected_raw_port, 'Port passed correctly (Network Byte Order preserved)';
# Verify IP
# IP is just a 32-bit int, raw
my $raw_addr = get_addr($sa);
my $expected_addr = unpack 'L', inet_aton($ip);
is $raw_addr, $expected_addr, 'IP address passed correctly';
done_testing;