Affix

 view release on metacpan or  search on metacpan

CONTRIBUTING.md  view on Meta::CPAN

2.  **Fork the Repository**: Start by forking the main repository to your own GitHub account.
3.  **Create a Branch**: Create a new branch for your feature or bugfix from the `main` branch. Please use a descriptive name.

    ```bash
    # For a new feature:
    git checkout -b feature/add-riscv-support
    # For a bug fix:
    git checkout -b fix/struct-classification-bug
    ```

4.  **Make Your Changes**: Write your code. Please adhere to the existing coding style and add comments to new or complex logic.
5.  **Test Your Changes**: A pull request is far more likely to be accepted if it includes tests. If you add new functionality, please add a corresponding test case.
6.  **Update the Changelog**: Add an entry to the `[Unreleased]` section of `CHANGELOG.md` describing your change.
7.  **Submit a Pull Request**: Push your branch to your fork and open a pull request against the `main` branch of the original repository. Please provide a clear description of your changes and link to the relevant issue (e.g., `Fixes #123`).

Changes.md  view on Meta::CPAN


### Added

- Float16 support
 - Added the Float16 keyword to Affix.pm.
 - Implemented float_to_half and half_to_float conversion logic in Affix.c (IEEE 754).
 - Added optimized opcodes (OP_PUSH_FLOAT16, OP_RET_FLOAT16) to the internal VM dispatcher for high-performance marshalling.

- Bitfield Support:
 - Enhanced Struct [...] syntax in Affix.pm to support bitfield widths (e.g., a => UInt32, 3).
 - Implemented bitmask-based marshalling in Affix.c to correctly pack and unpack C-style bitfields within structs.

- 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.

README.md  view on Meta::CPAN

# And release the memory. This is automatic when such a scalar falls out of scope
Affix::free($ptr);
```

# DESCRIPTION

Affix is a high-performance, developer friendly Foreign Function Interface (FFI) extension for Perl. It serves as a
universal bridge to the vast ecosystem of native software including those written in C, Rust, Zig, C++, Go, Fortran,
and more without writing XS code, managing a compiler, or compromising on execution speed. Affix also comes with an
extensive type system including native support for primitives (including half-width floats and 128bit integers), nested
C style structs, union, fixed size arrays, smart handling of enums, SIMD vector types, and, of course, pointers.

At its core, Affix is powered by [infix](https://github.com/sanko/infix/), a lightweight JIT (Just-In-Time) compilation
engine designed with speed and portability as its primary objectives. Unlike traditional FFI solutions that rely on
generic, per-call dispatch loops, Affix generates optimized machine code trampolines at runtime. These trampolines
handle argument marshalling and return value processing directly, significantly reducing the overhead of crossing the
boundary between Perl and native code. The underlying infix engine is [rigorously tested across a diverse range of
environments](https://github.com/sanko/infix/actions/workflows/ci.yml), ensuring reliable performance on Linux, Windows,
macOS, Solaris, and various BSD flavors. It supports multiple CPU architectures including `x86_64` and `AArch64`
(ARM64).

README.md  view on Meta::CPAN


## Callbacks & Functions

- **`Callback[ [$params] => $ret ]`**: Defines the signature of a C function pointer. Allows you to pass Perl subroutines into C functions.

    ```perl
    # C: void set_handler( void (*cb)(int) );
    affix $lib, 'set_handler', [ Callback[ [Int] => Void ] ] => Void;
    ```

- **`ThisCall( $cb_or_sig )`**: Helper for C++-style `__thiscall` callbacks. Prepends a `Pointer[Void]` (the `this` pointer) to the signature.

## Variadic Functions (VarArgs)

Affix supports C functions that take a variable number of arguments (e.g., `printf`, `ioctl`). When defining a
signature, use the `VarArgs` token at the end of the argument list.

### Basic Usage

```perl
# C: int printf(const char* format, ...);

infix/src/common/compat_c23.h  view on Meta::CPAN

 *
 * @internal
 */
#pragma once
#include "common/infix_config.h"
#include <infix/infix.h>
#include <stdbool.h>
#include <stddef.h>
/**
 * @def nullptr
 * @brief Defines `nullptr` as a standard C-style null pointer constant (`(void*)0`).
 *
 * @details This provides a consistent null pointer constant across C and C++
 * compilation environments. While `NULL` is standard in C, `nullptr` is preferred
 * in modern C++ and is being adopted in newer C standards. This macro ensures
 * the codebase can use `nullptr` consistently without causing compilation errors
 * in a strict C11/C17 environment.
 */
#if !defined(nullptr) && !defined(__cplusplus)
#define nullptr ((void *)0)
#endif

infix/src/common/double_tap.h  view on Meta::CPAN

#endif

#if defined(__GNUC__) || defined(__clang__)
#define DBLTAP_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define DBLTAP_NOINLINE __declspec(noinline)
#else
#define DBLTAP_NOINLINE
#endif

// Compiler-specific attribute for printf-style format checking.
#if defined(__GNUC__) || defined(__clang__)
#define DBLTAP_PRINTF_FORMAT(fmt_index, arg_index) __attribute__((format(printf, fmt_index, arg_index)))
#else
#define DBLTAP_PRINTF_FORMAT(fmt_index, arg_index)
#endif

// Public Test Harness Functions (wrapped by macros for convenience)
void tap_init(void);
void tap_plan(size_t count);
int tap_done(void);

infix/src/core/error.c  view on Meta::CPAN

 * @param code The specific error code.
 * @param position For parser errors, the byte offset into the signature string where the error occurred.
 */
void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position) {
    g_infix_last_error.category = category;
    g_infix_last_error.code = code;
    g_infix_last_error.position = position;
    g_infix_last_error.system_error_code = 0;
    // Check if we can generate a rich parser error message.
    if (category == INFIX_CATEGORY_PARSER && g_infix_last_signature_context != nullptr) {
        // Generate a rich, GCC-style error message for parser failures.
        const char * signature = g_infix_last_signature_context;
        size_t sig_len = strlen(signature);
        const size_t radius = 20;  // Number of characters to show around the error position.
        // Calculate the start and end of the snippet to display.
        size_t start = (position > radius) ? (position - radius) : 0;
        size_t end = (position + radius < sig_len) ? (position + radius) : sig_len;
        // Add indicators if the snippet is truncated.
        const char * start_indicator = (start > 0) ? "... " : "";
        const char * end_indicator = (end < sig_len) ? " ..." : "";
        size_t start_indicator_len = (start > 0) ? 4 : 0;

infix/src/core/signature.c  view on Meta::CPAN

 * @param[in,out] state The current parser state.
 * @param[in] code The error code to set.
 */
INFIX_INTERNAL void _infix_set_parser_error(parser_state * state, infix_error_code_t code) {
    _infix_set_error(INFIX_CATEGORY_PARSER, code, (size_t)(state->p - state->start));
}
INFIX_INTERNAL void skip_whitespace(parser_state * state);

/**
 * @internal
 * @brief Advances the parser's cursor past any whitespace or C-style line comments.
 * @param[in,out] state The parser state to modify.
 */
INFIX_INTERNAL void skip_whitespace(parser_state * state) {
    while (true) {
        while (isspace((unsigned char)*state->p))
            state->p++;
        if (*state->p == '#')  // C-style line comments
            while (*state->p != '\n' && *state->p != '\0')
                state->p++;
        else
            break;
    }
}
/**
 * @internal
 * @brief Parses an unsigned integer from the string, used for array/vector sizes.
 * @param[in,out] state The parser state.

infix/src/core/signature.c  view on Meta::CPAN

    if (val > SIZE_MAX) {
        _infix_set_parser_error(state, INFIX_CODE_INTEGER_OVERFLOW);
        return false;
    }
    *out_val = (size_t)val;
    state->p = end;
    return true;
}
/**
 * @internal
 * @brief Parses a C-style identifier from the string.
 * @details This is used for member names, named types, and function argument names.
 * It handles simple identifiers (`my_var`) and C++-style namespaces (`NS::Name`).
 * @param[in,out] state The parser state.
 * @return An arena-allocated string for the identifier, or `nullptr` on failure.
 */
static const char * parse_identifier(parser_state * state) {
    skip_whitespace(state);
    const char * start = state->p;
    if (!isalpha((unsigned char)*start) && *start != '_')
        return nullptr;
    while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
        if (*state->p == ':' && state->p[1] != ':')

infix/src/core/signature.c  view on Meta::CPAN

    if (consume_keyword(state, "float16"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_FLOAT16);
    if (consume_keyword(state, "float32"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_FLOAT);
    if (consume_keyword(state, "float64"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_DOUBLE);
    if (consume_keyword(state, "bool"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_BOOL);
    if (consume_keyword(state, "void"))
        return infix_type_create_void();
    // C-style convenience aliases
    if (consume_keyword(state, "uchar"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_UINT8);
    if (consume_keyword(state, "char"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_SINT8);
    if (consume_keyword(state, "ushort"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_UINT16);
    if (consume_keyword(state, "short"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_SINT16);
    if (consume_keyword(state, "uint"))
        return infix_type_create_primitive(INFIX_PRIMITIVE_UINT32);

infix/src/core/signature.c  view on Meta::CPAN

    size_t itanium_sub_count;      /**< Number of components in the dictionary. */
    // MSVC mangling state
    const infix_type * msvc_types[10]; /**< First 10 encountered types for back-referencing. */
    size_t msvc_type_count;            /**< Number of types encountered. */
} printer_state;
/**
 * @internal
 * @brief A safe `vsnprintf` wrapper for building the signature string.
 * Updates the printer state and sets an error on buffer overflow.
 * @param[in,out] state The printer state.
 * @param[in] fmt The `printf`-style format string.
 * @param[in] ... Arguments for the format string.
 */
static void _print(printer_state * state, const char * fmt, ...) {
    if (state->status != INFIX_SUCCESS)
        return;
    va_list args;
    va_start(args, fmt);
    int written = vsnprintf(state->p, state->remaining, fmt, args);
    va_end(args);
    if (written < 0 || (size_t)written >= state->remaining)

infix/src/core/type_registry.c  view on Meta::CPAN

 * @details This is distinct from the main signature `parser_state` because the
 * grammar for definitions (`@Name=...;`) is much simpler and can be handled
 * with a simpler, non-recursive parser.
 */
typedef struct {
    const char * p;     /**< The current position in the definition string. */
    const char * start; /**< The start of the definition string for error reporting. */
} _registry_parser_state_t;
/**
 * @internal
 * @brief Skips whitespace and C++-style line comments in a definition string.
 * @param state The parser state to advance.
 */
static void _registry_parser_skip_whitespace(_registry_parser_state_t * state) {
    while (1) {
        while (isspace((unsigned char)*state->p))
            state->p++;
        if (*state->p == '#')  // Skip comments
            while (*state->p != '\n' && *state->p != '\0')
                state->p++;
        else

lib/Affix.pod  view on Meta::CPAN


    # And release the memory. This is automatic when such a scalar falls out of scope
    Affix::free($ptr);

=head1 DESCRIPTION

Affix is a high-performance, developer friendly Foreign Function Interface (FFI) extension for Perl. It serves as a
universal bridge to the vast ecosystem of native software including those written in C, Rust, Zig, C++, Go, Fortran,
and more without writing XS code, managing a compiler, or compromising on execution speed. Affix also comes with an
extensive type system including native support for primitives (including half-width floats and 128bit integers), nested
C style structs, union, fixed size arrays, smart handling of enums, SIMD vector types, and, of course, pointers.

At its core, Affix is powered by L<infix|https://github.com/sanko/infix/>, a lightweight JIT (Just-In-Time) compilation
engine designed with speed and portability as its primary objectives. Unlike traditional FFI solutions that rely on
generic, per-call dispatch loops, Affix generates optimized machine code trampolines at runtime. These trampolines
handle argument marshalling and return value processing directly, significantly reducing the overhead of crossing the
boundary between Perl and native code. The underlying infix engine is L<rigorously tested across a diverse range of
environments|https://github.com/sanko/infix/actions/workflows/ci.yml>, ensuring reliable performance on Linux, Windows,
macOS, Solaris, and various BSD flavors. It supports multiple CPU architectures including C<x86_64> and C<AArch64>
(ARM64).

lib/Affix.pod  view on Meta::CPAN


=head2 Callbacks & Functions

=over

=item * B<C<< Callback[ [$params] => $ret ] >>>: Defines the signature of a C function pointer. Allows you to pass Perl subroutines into C functions.

    # C: void set_handler( void (*cb)(int) );
    affix $lib, 'set_handler', [ Callback[ [Int] => Void ] ] => Void;

=item * B<C<ThisCall( $cb_or_sig )>>: Helper for C++-style C<__thiscall> callbacks. Prepends a C<Pointer[Void]> (the C<this> pointer) to the signature.

=back

=head2 Variadic Functions (VarArgs)

Affix supports C functions that take a variable number of arguments (e.g., C<printf>, C<ioctl>). When defining a
signature, use the C<VarArgs> token at the end of the argument list.

=head3 Basic Usage



( run in 2.800 seconds using v1.01-cache-2.11-cpan-5a3173703d6 )