Affix

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

# Find the address of the library's custom free function
my $free_func = find_symbol($my_lib, 'custom_free');

# When $ptr goes out of scope, Affix will call custom_free($ptr)
attach_destructor($ptr, $free_func, $my_lib);
```

### `readonly( $pin, [$bool] )`

Gets or sets the "const" status of a memory pin. If set to true, any attempt to write to that memory from Perl will
throw an exception.

```
readonly($point, 1);
$point->{x} = 20; # CRASH: Modification of a read-only C value
```

## Type Casting

### `cast( $ptr, $type )`

README.md  view on Meta::CPAN

These functions accept either Pins or raw integer addresses.

- `memcpy( $dest, $src, $bytes )`: Copies exactly `$bytes` from `$src` to `$dest`.
- `memmove( $dest, $src, $bytes )`: Copies `$bytes` from `$src` to `$dest`. Safe to use if the memory regions overlap.
- `memset( $ptr, $byte_val, $bytes )`: Fills the first `$bytes` of the memory block with the value `$byte_val`.
- `memcmp( $ptr1, $ptr2, $bytes )`: Compares the first `$bytes` of two memory blocks. Returns an integer less than, equal to, or greater than zero.
- `memchr( $ptr, $byte_val, $bytes )`: Locates the first occurrence of `$byte_val` within the first `$bytes` of the memory block. Returns a new Pin pointing to the match, or `undef`.

# `Const` & Readonly Memory

Enforce C's const contract at the Perl level. Affix intercepts writes to read-only memory and throws a fatal exception:
`Modification of a read-only C value attempted`.

## Declarative Const: `Const[ $type ]`

You can wrap any type in `Const[ ... ]` within a signature.

```perl
# C: void process(const char* name, const int* values);
affix $lib, 'process', [ Const[String], Pointer[ Const[Int] ] ] => Void;
```

README.md  view on Meta::CPAN

typedef Task => Struct[ id => Int, name => String ];
affix $lib, 'get_tasks', [] => Pointer[ Array[ Task(), 10 ] ];

my $tasks = get_tasks();
$tasks->[5]{id} = 404; # Writes directly to C memory!
```

### Deep Null Safety

Traversing a \`NULL\` pointer in C causes a segfault. Affix wraps C memory in Perl safety rails. If you try to traverse a
\`NULL\` pointer inside a struct, Affix intercepts it and throws a standard Perl exception (`Can't use an undefined
value as a HASH reference`).

# LIBRARIES & SYMBOLS

Load and inspect dynamic libraries across platforms. Affix's smart discovery engine handles varying extensions,
prefixes, and search paths automatically.

## Library Discovery

When you provide a bare library name (e.g., `'z'`, `'ssl'`, `'user32'`) rather than an absolute path, Affix

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

 * @endcode
 */
INFIX_API INFIX_NODISCARD infix_status infix_forward_create(infix_forward_t **,
                                                            const char *,
                                                            void *,
                                                            infix_registry_t *);
/**
 * @brief Creates a "safe" bound forward trampoline that catches native exceptions.
 * @details This is identical to `infix_forward_create`, but the generated trampoline
 *          is wrapped in a platform-specific exception handler (e.g., SEH on Windows).
 *          If the target function throws an exception, the trampoline will catch it
 *          and set the thread-local error to `INFIX_CODE_NATIVE_EXCEPTION`.
 *
 * @param[out] out_trampoline Receives the created handle.
 * @param[in] signature The function signature.
 * @param[in] target_function The address of the C function.
 * @param[in] registry An optional type registry.
 * @return `INFIX_SUCCESS` on success.
 */
INFIX_API INFIX_NODISCARD infix_status infix_forward_create_safe(infix_forward_t **,
                                                                 const char *,

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

} infix_error_category_t;
/**
 * @brief Enumerates specific error codes.
 */
typedef enum {
    // General Codes (0-99)
    INFIX_CODE_SUCCESS = 0,      /**< No error occurred. */
    INFIX_CODE_UNKNOWN,          /**< An unspecified error occurred. */
    INFIX_CODE_NULL_POINTER,     /**< A required pointer argument was NULL. */
    INFIX_CODE_MISSING_REGISTRY, /**< A type registry was required but not provided. */
    INFIX_CODE_NATIVE_EXCEPTION, /**< A native exception (C++/SEH) was thrown during execution. */

    // Allocation Codes (100-199)
    INFIX_CODE_OUT_OF_MEMORY = 100,       /**< A call to `malloc`, `calloc`, etc. failed. */
    INFIX_CODE_EXECUTABLE_MEMORY_FAILURE, /**< Failed to allocate executable memory from the OS. */
    INFIX_CODE_PROTECTION_FAILURE,        /**< Failed to change memory protection flags (e.g., `mprotect`). */
    INFIX_CODE_INVALID_ALIGNMENT,         /**< An invalid alignment (0 or not power-of-two) was requested. */

    // Parser Codes (200-299)
    INFIX_CODE_UNEXPECTED_TOKEN = 200,   /**< Encountered an unexpected character or token. */
    INFIX_CODE_UNTERMINATED_AGGREGATE,   /**< A struct, union, or array was not properly closed. */

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

    switch (code) {
    case INFIX_CODE_SUCCESS:
        return "Success";
    case INFIX_CODE_UNKNOWN:
        return "An unknown error occurred";
    case INFIX_CODE_NULL_POINTER:
        return "A required pointer argument was NULL";
    case INFIX_CODE_MISSING_REGISTRY:
        return "A type registry was required but not provided";
    case INFIX_CODE_NATIVE_EXCEPTION:
        return "A native exception was thrown across the FFI boundary";
    case INFIX_CODE_OUT_OF_MEMORY:
        return "Out of memory";
    case INFIX_CODE_EXECUTABLE_MEMORY_FAILURE:
        return "Failed to allocate executable memory";
    case INFIX_CODE_PROTECTION_FAILURE:
        return "Failed to change memory protection flags";
    case INFIX_CODE_INVALID_ALIGNMENT:
        return "Invalid alignment requested (must be power of two > 0)";
    case INFIX_CODE_UNEXPECTED_TOKEN:
        return "Unexpected token or character";

lib/Affix.pod  view on Meta::CPAN


    # Find the address of the library's custom free function
    my $free_func = find_symbol($my_lib, 'custom_free');

    # When $ptr goes out of scope, Affix will call custom_free($ptr)
    attach_destructor($ptr, $free_func, $my_lib);

=head3 C<readonly( $pin, [$bool] )>

Gets or sets the "const" status of a memory pin. If set to true, any attempt to write to that memory from Perl will
throw an exception.

    readonly($point, 1);
    $point->{x} = 20; # CRASH: Modification of a read-only C value

=head2 Type Casting

=head3 C<cast( $ptr, $type )>

The most powerful tool in the memory kit. It "overlays" a C type definition onto a raw memory address.

lib/Affix.pod  view on Meta::CPAN

=item * C<memset( $ptr, $byte_val, $bytes )>: Fills the first C<$bytes> of the memory block with the value C<$byte_val>.

=item * C<memcmp( $ptr1, $ptr2, $bytes )>: Compares the first C<$bytes> of two memory blocks. Returns an integer less than, equal to, or greater than zero.

=item * C<memchr( $ptr, $byte_val, $bytes )>: Locates the first occurrence of C<$byte_val> within the first C<$bytes> of the memory block. Returns a new Pin pointing to the match, or C<undef>.

=back

=head1 C<Const> & Readonly Memory

Enforce C's const contract at the Perl level. Affix intercepts writes to read-only memory and throws a fatal exception:
C<Modification of a read-only C value attempted>.

=head2 Declarative Const: C<Const[ $type ]>

You can wrap any type in C<Const[ ... ]> within a signature.

    # C: void process(const char* name, const int* values);
    affix $lib, 'process', [ Const[String], Pointer[ Const[Int] ] ] => Void;

=head2 Imperative Const: C<readonly( $pin, [$bool] )>

lib/Affix.pod  view on Meta::CPAN


    typedef Task => Struct[ id => Int, name => String ];
    affix $lib, 'get_tasks', [] => Pointer[ Array[ Task(), 10 ] ];

    my $tasks = get_tasks();
    $tasks->[5]{id} = 404; # Writes directly to C memory!

=head3 Deep Null Safety

Traversing a `NULL` pointer in C causes a segfault. Affix wraps C memory in Perl safety rails. If you try to traverse a
`NULL` pointer inside a struct, Affix intercepts it and throws a standard Perl exception (C<Can't use an undefined
value as a HASH reference>).

=head1 LIBRARIES & SYMBOLS

Load and inspect dynamic libraries across platforms. Affix's smart discovery engine handles varying extensions,
prefixes, and search paths automatically.

=head2 Library Discovery

When you provide a bare library name (e.g., C<'z'>, C<'ssl'>, C<'user32'>) rather than an absolute path, Affix

t/035_magic_struct.t  view on Meta::CPAN

subtest 'Out of bounds (C-style)' => sub {

    # In C, pointers don't have bounds. Our magical system should behave like C.
    # To prevent ACTUAL heap corruption (which crashes the Perl allocator
    # during global destruction), we allocate enough backing memory here.
    my $ptr = Affix::malloc( 15 * sizeof(Int) );
    $ptr = cast( $ptr, Pointer [Int] );

    # This is "dangerous" but should work without a Perl exception
    # because it's just pointer arithmetic.
    #~ ok lives { $ptr->[10] = 0 }, 'Accessing out-of-bounds index does not throw Perl exception';
    # Since pointers have no bounds and we avoid `tie`, pointer arithmetic
    # is done explicitly via ptr_add.
    my $p10 = ptr_add( $ptr, 10 * sizeof(Int) );
    ok lives { $$p10 = 0 }, 'Accessing out-of-bounds memory via ptr_add does not throw Perl exception';
};
#
done_testing;

t/999_marshaller_2.t  view on Meta::CPAN


    # Traversing a NULL pointer in a struct
    $comp->{manager} = undef;    # Set C pointer to NULL
    like dies { $comp->{manager}{name} }, qr[undefined value], 'Accessing NULL pointer member is a fatal exception';

    # returning NULL
    ok my $null_comp = get_null_company(), 'C returns pointer to struct with NULL member';
    is $null_comp->{manager}, undef, 'C NULL pointer correctly becomes Perl undef';

    # Deep Null
    like dies { $null_comp->{manager}{tasks}[0]{id} }, qr[undefined value], 'Deep access on C NULL throws Perl exception';
};
subtest 'Giant Array & Anon Types' => sub {
    my $type = Struct [ a => Int, b => Int ];
    my $mem  = alloc_owned( sizeof($type) );

    # TEST ANONYMOUS TYPE EVAPORATION
    {
        my $p = cast( $mem, $type );
        is $p->{a}, 0, 'Anonymous struct works';
    }



( run in 1.806 second using v1.01-cache-2.11-cpan-b16cb0d3907 )