Affix

 view release on metacpan or  search on metacpan

Changes.md  view on Meta::CPAN


All notable changes to Affix.pm will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.0.2] - 2025-12-14

### Changed

    - In an attempt to debug mystery failures in SDL3.pm, Affix.pm will warn and return `undef` instead of `croak`ing.
    - Improved error reporting: if the internal error message is empty, the numeric error code is now included in the warning.

### Fixed

    - [infix] Fixed a critical file descriptor leak on POSIX platforms (Linux/FreeBSD) where the file descriptor returned by `shm_open` was kept open for the lifetime of the trampoline, eventually hitting the process file descriptor limit (EMFILE). T...
    - Fixed memory leaks that occurred when trampoline creation failed midway (cleaning up partial arenas, strings, and backend structures).

## [v1.0.1] - 2025-12-13

### Changed

README.md  view on Meta::CPAN

Standard C memory operations are available for high-performance manipulation of Pins.

- `memchr( $ptr, $ch, $count )`
- `memcmp( $lhs, $rhs, $count )`
- `memset( $dest, $ch, $count )`
- `memcpy( $dest, $src, $count )`
- `memmove( $dest, $src, $count )`

## `dump( $ptr, $length )`

Dumps `$length` bytes of raw data from a given point in memory to STDOUT in a hex editor style. Useful for debugging
layout issues.

# INTROSPECTION

## `sizeof( ... )`

```perl
my $size = sizeof( Int );
my $size_rect = sizeof( Struct[ x => Int, y => Int ] );
```

README.md  view on Meta::CPAN


# UTILITIES

## `get_system_error()`

Returns the `errno` (Linux/Unix) or `GetLastError` (Windows) from the most recent FFI call. This must be called
immediately after the function invokes to ensure accuracy.

## `sv_dump( $sv )`

Dumps the internal structure of a Perl scalar to STDERR. Useful for debugging Perl internals.

# COMPILER WRAPPER

Affix includes a lightweight, cross-platform C compiler wrapper `Affix::Compiler`. This is useful for compiling small
C stubs or "glue" code at runtime to bridge complex macros or inline functions that cannot be bound directly.

```perl
use Affix;
my $compiler = Affix::Compiler->new(
    name   => 'my_wrapper',

builder/Affix/Builder.pm  view on Meta::CPAN

    use JSON::PP 2            qw[encode_json decode_json];
    use File::Temp            qw[tempfile];

    # Not in CORE
    use Path::Tiny qw[path cwd];
    use ExtUtils::Helpers 0.028 qw[make_executable split_like_shell detildefy];

    # infix and Affix stuff
    use Config qw[%Config];
    field $force : param //= 0;
    field $debug : param = 0;
    field $libver;
    field $cflags;
    field $ldflags;
    field $cppver = 'c++17';    # https://en.wikipedia.org/wiki/C%2B%2B20#Compiler_support
    field $cver   = 'c17';      # https://en.wikipedia.org/wiki/C17_(C_standard_revision)
    field $make : param //= $Config{make};
    #
    field $action : param //= 'build';
    field $meta : reader = CPAN::Meta->load_file('META.json');

builder/Affix/Builder.pm  view on Meta::CPAN

    field $prefix        : param    //= '';
    #
    ADJUST {
        -e 'META.json' or die "No META information provided\n";

        # Configure Flags
        my $is_bsd = $^O =~ /bsd/i;
        my $is_win = $^O =~ /MSWin32/i;
        $cflags  = $is_bsd ? '' : '-fPIC ';
        $ldflags = $is_bsd ? '' : ' -flto=auto ';
        if ( $debug > 0 ) {
            $cflags
                .= '-DDEBUG=' .
                $debug .
                ' -g3 -gdwarf-4 ' .
                ' -Wno-deprecated -pipe ' .
                ' -Wall -Wextra -Wpedantic -Wvla -Wnull-dereference ' .
                ' -Wswitch-enum  -Wduplicated-cond ' .
                ' -Wduplicated-branches';
            $cflags .= ' -fvar-tracking-assignments' unless $Config{osname} eq 'darwin';
        }
        elsif ( !$is_win ) {
            $cflags
                .= ' -DNDEBUG -DBOOST_DISABLE_ASSERTS -Ofast -ftree-vectorize -ffast-math -fno-align-functions -fno-align-loops -fno-omit-frame-pointer -flto=auto';

builder/Affix/Builder.pm  view on Meta::CPAN

                quiet        => 0,
                'C++'        => $cxx,
                source       => $source->stringify,
                defines      => { VERSION => qq/"$version"/, XS_VERSION => qq/"$version"/ },
                include_dirs => [
                    cwd->stringify,                                             cwd->child('infix')->realpath->stringify,
                    cwd->child('infix')->child('include')->realpath->stringify, cwd->child('infix')->child('src')->realpath->stringify,
                    $source->dirname,                                           $pre->child( $meta->name, 'include' )->stringify
                ],
                extra_compiler_flags =>
                    ( '-fPIC -std=' . ( $cxx ? $cppver : $cver ) . ' ' . $cflags . ( $debug ? ' -ggdb3 -g -Wall -Wextra -pedantic' : '' ) )
                ) :
                $obj;
        }

        # Point to the Architecture-specific build lib
        my $infix_build_lib = cwd->absolute->child('infix')->child( 'build_lib', $Config{archname} )->stringify;

        # Check for -lrt requirement
        my $lrt_flag = $self->check_for_lrt();
        my $data     = {

infix/include/infix/infix.h  view on Meta::CPAN

 * @brief APIs for inspecting and serializing the contents of a named type registry.
 * @ingroup high_level_api
 * @{
 */
/** @brief An opaque handle to a registry iterator. Created by `infix_registry_iterator_begin`. */
typedef struct infix_registry_iterator_t infix_registry_iterator_t;
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small.
 */
c23_nodiscard infix_status infix_registry_print(char *, size_t, const infix_registry_t *);
/**
 * @brief Initializes an iterator for traversing the types in a registry.

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

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file utility.h
 * @brief A header for conditionally compiled debugging utilities.
 * @ingroup internal_utils
 *
 * @internal
 * This header is the central point for the library's internal debugging infrastructure.
 * Its primary feature is that it behaves differently based on the `INFIX_DEBUG_ENABLED`
 * preprocessor macro. This allows debugging code to be seamlessly integrated
 * during development without affecting the performance or size of the final
 * production binary.
 *
 * - **When `INFIX_DEBUG_ENABLED` is defined and non-zero (Debug Mode):**
 *   - It declares the `infix_dump_hex` function for detailed memory inspection.
 *   - It defines the `INFIX_DEBUG_PRINTF` macro, which integrates with the `double_tap`
 *     test harness's logging system (`note()`) if available, or falls back to a
 *     standard `printf`. This allows debug messages from the core library to appear
 *     cleanly within the test output.
 *
 * - **When `INFIX_DEBUG_ENABLED` is not defined or is zero (Release Mode):**
 *   - All debugging macros are defined as no-ops (`((void)0)`).
 *   - The `infix_dump_hex` function is defined as an empty `static inline` function.
 *   - This design ensures that all debugging code and calls are completely compiled
 *     out by the optimizer, resulting in zero overhead in release builds.
 * @endinternal
 */
#include "common/compat_c23.h"
#include <stddef.h>
// Check if INFIX_DEBUG_ENABLED is defined and set to a non-zero value.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
// The double_tap framework is only included if both debug mode AND the main
// test harness toggle are enabled. This allows for debug builds of non-test executables.
#if defined(DBLTAP_ENABLE)
#include "common/double_tap.h"
/**
 * @internal
 * @def INFIX_DEBUG_PRINTF(...)
 * @brief A macro for printing formatted debug messages during a debug build with the test harness.
 * @details In debug builds where `double_tap.h` is active, this macro wraps the `note()`
 *          macro, integrating debug output from the library's internals cleanly into the
 *          TAP-formatted test logs.
 * @example
 * ```c
 * INFIX_DEBUG_PRINTF("Processing type %d with size %zu", type->id, type->size);
 * ```
 */
#define INFIX_DEBUG_PRINTF(...) note("INFIX_DEBUG: " __VA_ARGS__)
#else
#include <stdio.h>
/**
 * @internal
 * @def INFIX_DEBUG_PRINTF(...)
 * @brief A macro for printing formatted debug messages (printf fallback).
 * @details In debug builds where the `double_tap.h` harness is *not* active (e.g., when
 *          building a standalone example program), this macro falls back to a standard
 *          `printf`, ensuring that debug messages are still visible.
 */
#define INFIX_DEBUG_PRINTF(...)                \
    do {                                       \
        printf("# INFIX_DEBUG: " __VA_ARGS__); \
        printf("\n");                          \
    } while (0)
#endif  // DBLTAP_ENABLE
/**
 * @internal
 * @brief Declares the function prototype for `infix_dump_hex` for use in debug builds.
 * @details This function is an invaluable tool for inspecting the raw machine code generated
 *          by the JIT compiler or examining the memory layout of complex structs. It prints
 *          a detailed hexadecimal and ASCII dump of a memory region to the standard
 *          output, formatted for readability.
 *
 * @param data A pointer to the start of the memory block to dump.
 * @param size The number of bytes to dump.
 * @param title A descriptive title to print before and after the hex dump.
 */
void infix_dump_hex(const void * data, size_t size, const char * title);
#else  // INFIX_DEBUG_ENABLED is NOT defined or is zero (Release Mode)
/**
 * @internal
 * @def INFIX_DEBUG_PRINTF(...)
 * @brief A no-op macro for printing debug messages in release builds.
 * @details In release builds, this macro is defined as `((void)0)`, a standard C idiom
 *          for creating a statement that does nothing and has no side effects. The
 *          compiler will completely remove any calls to it, ensuring zero performance impact.
 */
#define INFIX_DEBUG_PRINTF(...) ((void)0)
/**
 * @internal
 * @brief A no-op version of `infix_dump_hex` for use in release builds.
 * @details This function is defined as an empty `static inline` function.
 *          - `static`: Prevents linker errors if this header is included in multiple files.

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>

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

        }
    }
    else if (buffer_size > 0)
        buffer[buffer_size - 1] = '\0';
    return state.status;
}
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * @details The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined. The order of
 * definitions in the output string is not guaranteed.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small
 *         or another error occurs.
 */
c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {

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

 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file utility.c
 * @brief Implements internal debugging utilities.
 * @ingroup internal_core
 *
 * @details This file provides the implementation for debugging functions that are
 * conditionally compiled only when the `INFIX_DEBUG_ENABLED` preprocessor macro
 * is defined and set to a non-zero value.
 *
 * In release builds, this entire translation unit is effectively empty, ensuring
 * that debugging code has no impact on the final binary's size or performance.
 * The corresponding function declarations in `utility.h` become empty inline
 * stubs, which are optimized away by the compiler.
 */
// This file is only compiled if debugging is enabled.
#if defined(INFIX_DEBUG_ENABLED) && INFIX_DEBUG_ENABLED
// Use the double-tap test harness's `note` macro for debug printing if available.
// This integrates the debug output seamlessly into the TAP test logs.
#if defined(DBLTAP_ENABLE) && defined(DBLTAP_IMPLEMENTATION)
#include "common/double_tap.h"
#else
// If not building as part of a test, fall back to a standard printf implementation.
#include <stdio.h>
#ifndef note
#define note(...)                 \
    do {                          \
        printf("# " __VA_ARGS__); \
        printf("\n");             \
    } while (0)
#endif
#endif  // DBLTAP_ENABLE
#include "common/utility.h"
#include <inttypes.h>
/**
 * @internal
 * @brief Dumps a block of memory to standard output in a standard hexadecimal format.
 *
 * @details This is an essential debugging tool for inspecting the machine code generated
 * by the JIT engine or for examining the memory layout of complex data structures.
 * The output is formatted similarly to common hex dump utilities, with a 16-byte
 * width, address offsets, hexadecimal bytes, and an ASCII representation.
 *
 * This function is only compiled in debug builds.
 *
 * @param data A pointer to the memory to dump.
 * @param size The number of bytes to dump.
 * @param title A descriptive title to print before the hex dump.
 */
void infix_dump_hex(const void * data, size_t size, const char * title) {
    const uint8_t * byte = (const uint8_t *)data;
    char line_buf[256];
    char * buf_ptr;
    size_t remaining_len;

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

 * rest of the source tree. It is the entry point for the build system.
 * @endinternal
 */
// 1. Error Handling: Provides the thread-local error reporting system.
//    (No dependencies on other infix modules).
#include "core/error.c"
// 2. Arena Allocator: The fundamental memory management component.
//    (Depends only on malloc/free).
#include "core/arena.c"
// 3. OS Executor: Handles OS-level memory management for executable code.
//    (Depends on error handling, debugging utilities).
#include "jit/executor.c"
// 4. Type Registry: Manages named types.
//    (Depends on arena for storage and signature parser for definitions).
#include "core/type_registry.c"
// 5. Signature Parser: Implements the high-level string-based API.
//    (Depends on types, arena, and registry).
#include "core/signature.c"
// 6. Dynamic Library Loader: Implements cross-platform `dlopen`/`dlsym`.
//    (Depends on error handling, types, and arena).
#include "core/loader.c"

infix/src/jit/executor.c  view on Meta::CPAN

    return exec;
}
/**
 * @internal
 * @brief Frees a block of executable memory with use-after-free hardening.
 *
 * @details Before freeing the memory, this function first attempts to change the
 * memory protection to be inaccessible (`PROT_NONE` or `PAGE_NOACCESS`). This
 * creates a "guard page" that will cause an immediate, safe crash if a dangling
 * pointer to the freed trampoline is ever used, making use-after-free bugs
 * much easier to detect and debug.
 *
 * @param exec The executable memory block to free.
 */
void infix_executable_free(infix_executable_t exec) {
    if (exec.size == 0)
        return;
#if defined(INFIX_OS_WINDOWS)
    if (exec.rw_ptr) {
        // Change protection to NOACCESS to catch use-after-free bugs immediately.
        if (!VirtualProtect(exec.rw_ptr, exec.size, PAGE_NOACCESS, &(DWORD){0}))

lib/Affix.pm  view on Meta::CPAN

    use Config                qw[%Config];
    use File::Spec::Functions qw[rel2abs canonpath curdir path catdir];
    use File::Basename        qw[basename dirname];
    use File::Find            qw[find];
    use File::Temp            qw[tempdir];
    #
    my $okay = 0;
    #
    BEGIN {
        use XSLoader;
        $DynaLoad::dl_debug = 1;
        $okay               = XSLoader::load();
        my $platform
            = 'Affix::Platform::' .
            ( ( $^O eq 'MSWin32' ) ? 'Windows' :
                $^O eq 'darwin'                                                                   ? 'MacOS' :
                ( $^O eq 'freebsd' || $^O eq 'openbsd' || $^O eq 'netbsd' || $^O eq 'dragonfly' ) ? 'BSD' :
                'Unix' );

        #~ warn $platform;
        #~ use base $platform;

lib/Affix.pod  view on Meta::CPAN

=item C<memset( $dest, $ch, $count )>

=item C<memcpy( $dest, $src, $count )>

=item C<memmove( $dest, $src, $count )>

=back

=head2 C<dump( $ptr, $length )>

Dumps C<$length> bytes of raw data from a given point in memory to STDOUT in a hex editor style. Useful for debugging
layout issues.

=head1 INTROSPECTION

=head2 C<sizeof( ... )>

    my $size = sizeof( Int );
    my $size_rect = sizeof( Struct[ x => Int, y => Int ] );

Returns the size, in bytes, of the Type passed to it.

lib/Affix.pod  view on Meta::CPAN


=head1 UTILITIES

=head2 C<get_system_error()>

Returns the C<errno> (Linux/Unix) or C<GetLastError> (Windows) from the most recent FFI call. This must be called
immediately after the function invokes to ensure accuracy.

=head2 C<sv_dump( $sv )>

Dumps the internal structure of a Perl scalar to STDERR. Useful for debugging Perl internals.

=head1 COMPILER WRAPPER

Affix includes a lightweight, cross-platform C compiler wrapper C<Affix::Compiler>. This is useful for compiling small
C stubs or "glue" code at runtime to bridge complex macros or inline functions that cannot be bound directly.

    use Affix;
    my $compiler = Affix::Compiler->new(
        name   => 'my_wrapper',
        source => [ 'wrapper.c' ]

lib/Affix/Platform/Windows.pm  view on Meta::CPAN

            $clibname = 'msvcrt';
        }
        elsif ( $version <= 13 ) {
            $clibname = sprintf( 'msvcr%d', $version * 10 );
        }
        else {
            # CRT not directly loadable (see python/cpython#23606)
            return undef;
        }

        # Check for debug build
        my $debug_suffix = '_d';    # Assuming debug suffix is '_d'
        my $suffixes     = join '|', map quotemeta, @DynaLoader::dl_extensions;
        if ( $debug_suffix =~ /$suffixes/ ) {
            $clibname .= $debug_suffix;
        }
        return "$clibname.dll";
    }

    sub get_msvcrt_version() {
        open( my $pipe, '-|', 'dumpbin /headers msvcrt.dll', 'r' ) or return;
        my $dumpbin_output;
        $dumpbin_output .= $_ while <$pipe>;
        close $pipe;
        return $1 if $dumpbin_output && $dumpbin_output =~ /FileVersion\s+(\d+\.\d+\.\d+\.\d+)/;



( run in 1.232 second using v1.01-cache-2.11-cpan-0371d4a6215 )