Affix
view release on metacpan or search on metacpan
# 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 )`
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;
```
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 )