Affix

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

Allocates memory for an array of `$count` elements of the given `$type`, and zero-initializes the memory. Returns a
managed pin typed as an Array.

```perl
my $arr = calloc( 10, Int );
$arr->[0] = 42; # Write directly to the first element
```

### `realloc( $ptr, $new_size )`

Resizes the memory area pointed to by `$ptr` to `$new_size` bytes. The original pin is updated automatically
in-place.

```
$ptr = realloc( $ptr, 2048 );
```

### `strdup( $string )`

Allocates managed memory and copies the Perl string (along with a `NULL` terminator) into it. Returns a managed
`Pointer[Char]` pin.

infix/src/arch/x64/abi_sysv_x64.c  view on Meta::CPAN

/**
 * @internal
 * @brief Recursively classifies the eightbytes of an aggregate type.
 * @details This is the core of the complex System V classification algorithm. It traverses
 * the fields of a struct/array, examining each 8-byte chunk ("eightbyte") and assigning it a
 * class (INTEGER, SSE, MEMORY). The classification is "merged" according to ABI rules
 * (e.g., if an eightbyte contains both INTEGER and SSE parts, it becomes INTEGER).
 *
 * @param type The type of the current member/element being examined.
 * @param offset The byte offset of this member from the start of the aggregate.
 * @param[in,out] classes An array of two `arg_class_t` that is updated during classification.
 * @param depth The current recursion depth (to prevent stack overflow on malicious input).
 * @param field_count A counter to prevent DoS from excessively complex types.
 * @param is_bitfield True if the current member is a bitfield.
 * @return `true` if a condition forcing MEMORY classification is found, `false` otherwise.
 */
static bool classify_recursive(
    const infix_type * type, size_t offset, arg_class_t classes[2], int depth, size_t * field_count, bool is_bitfield) {
    // A recursive call can be made with a NULL type (e.g., from a malformed array from fuzzer).
    if (type == nullptr)
        return false;  // Terminate recusion path.

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


/**
 * @internal
 * @brief The internal implementation of the type-to-string printer for standard signatures.
 *
 * This function recursively walks a type graph and prints its signature representation.
 * The key feature is the initial check for `type->name`. If a semantic alias exists,
 * it is always preferred, ensuring that introspection and serialization produce
 * canonical, readable output (e.g., printing "@MyHandle" instead of "*void").
 *
 * @param[in,out] state The printer state, which is updated as the string is built.
 * @param[in] type The `infix_type` to print.
 */
static void _infix_type_print_signature_recursive(printer_state * state, const infix_type * type) {
    if (state->status != INFIX_SUCCESS || !type) {
        if (state->status == INFIX_SUCCESS)
            state->status = INFIX_ERROR_INVALID_ARGUMENT;
        return;
    }
    // If the type has a semantic name, always prefer printing it.
    if (type->name) {

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

    type->category = INFIX_TYPE_STRUCT;  // Packed structs are still fundamentally structs.
    type->meta.aggregate_info.members = arena_members;
    type->meta.aggregate_info.num_members = num_members;
    type->meta.aggregate_info.is_packed = true;  // Marked as packed
    *out_type = type;
    return INFIX_SUCCESS;
}
/**
 * @brief Creates a placeholder for a named type that will be resolved later by a type registry.
 * @details This is a key component for defining recursive or mutually-dependent types.
 * The created type has a size and alignment of 0/1, which are updated during the
 * "Resolve" and "Layout" stages of the pipeline.
 * @param[in] arena The arena for allocation.
 * @param[out] out_type A pointer to receive the new `infix_type`.
 * @param[in] name The name of the type (e.g., "MyStruct").
 * @param[in] agg_cat The expected category of the aggregate (struct or union).
 * @return `INFIX_SUCCESS` on success.
 */
INFIX_API c23_nodiscard infix_status infix_type_create_named_reference(infix_arena_t * arena,
                                                                       infix_type ** out_type,
                                                                       const char * name,

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

    case INFIX_TYPE_STRUCT:
    case INFIX_TYPE_UNION:
        for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
            _infix_type_recalculate_layout_recursive(
                temp_arena, type->meta.aggregate_info.members[i].type, visited_head);
        }
        break;
    default:
        break;  // Other types have no child types to recurse into.
    }
    // After children are updated, recalculate this type's layout.
    if (type->category == INFIX_TYPE_STRUCT)
        _layout_struct(type);
    else if (type->category == INFIX_TYPE_UNION) {
        size_t max_size = 0;
        size_t max_alignment = 1;
        for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
            infix_type * member_type = type->meta.aggregate_info.members[i].type;
            if (member_type->size > max_size)
                max_size = member_type->size;
            if (member_type->alignment > max_alignment)

lib/Affix.c  view on Meta::CPAN


        affix->plan[i].executor = get_plan_step_executor(original_type);
        affix->plan[i].opcode = get_opcode_for_type(original_type);
        affix->plan[i].data.type = original_type;  // Now points to persistent memory
        affix->plan[i].data.index = i;

        if (original_type->category == INFIX_TYPE_POINTER) {
            const infix_type * pointee = original_type->meta.pointer_info.pointee_type;
            const char * pointee_name = infix_type_get_name(pointee);
            // Skip writeback for Pointer[@SV] to avoid corrupting Perl variables with void return values
            // We assume SV* passed to C is owned by C for the duration and shouldn't be auto-updated
            // (since the SV* itself is the value, not a pointer to a value we want copied back).
            bool is_sv_pointer = pointee_name && (strEQ(pointee_name, "SV") || strEQ(pointee_name, "@SV"));

            if (!is_sv_pointer && pointee->category != INFIX_TYPE_REVERSE_TRAMPOLINE &&
                pointee->category != INFIX_TYPE_VOID) {
                temp_out_info[out_param_count].perl_stack_index = i;
                temp_out_info[out_param_count].pointee_type = pointee;
                temp_out_info[out_param_count].writer = get_out_param_writer(pointee);
                out_param_count++;
            }

lib/Affix.pod  view on Meta::CPAN

=head3 C<calloc( $count, $type )>

Allocates memory for an array of C<$count> elements of the given C<$type>, and zero-initializes the memory. Returns a
managed pin typed as an Array.

    my $arr = calloc( 10, Int );
    $arr->[0] = 42; # Write directly to the first element

=head3 C<realloc( $ptr, $new_size )>

Resizes the memory area pointed to by C<$ptr> to C<$new_size> bytes. The original pin is updated automatically
in-place.

    $ptr = realloc( $ptr, 2048 );

=head3 C<strdup( $string )>

Allocates managed memory and copies the Perl string (along with a C<NULL> terminator) into it. Returns a managed
C<Pointer[Char]> pin.

    my $str_ptr = strdup("Hello C!");

t/007_pointers.t  view on Meta::CPAN

# Read the entire array from C into a Perl variable
my $array_values = $$arr_ptr;

# Modify perl's copy
$array_values->[0] = 10;
$array_values->[7] = 80;

# Write the entire modified array ref back to the C pointer
$$arr_ptr = $array_values;

# Visual evidence that the memory has actually been updated
#~ Affix::dump( $arr_ptr, 32 );
# sum_int_array takes *int, so passing [8:int] (array ref) works as pointer
ok affix( $lib_path, 'sum_int_array', [ Pointer [Int], Int ], Int ), 'affix ... "sum_int_array", ...';
is sum_int_array( $arr_ptr, 8 ), 90, 'realloc successfully resized memory';
#
isa_ok my $check_is_null = wrap( $lib_path, 'check_is_null', '(*void)->bool' ), ['Affix'];
ok $check_is_null->(undef), 'Passing undef to a *void argument is received as NULL';
subtest 'char*' => sub {
    isa_ok my $get_string = wrap( $lib_path, 'get_hello_string', '()->*char' ), ['Affix'];
    is $get_string->(), 'Hello from C', 'Correctly returned a C string';

t/026_context.t  view on Meta::CPAN

#
my $context = { id => 1, count => 0 };

# This verifies that spawn correctly accepts Perl SVs as "SVPtr" (aliased Pointer[SV])
spawn( $context, $step1 );

# This verifies that run_scheduler correctly calls the callback,
# and the callback correctly receives SVs back from C.
run_scheduler($wrapper);
#
is $context->{count}, 3, 'All steps executed and context updated';
#
done_testing();



( run in 1.905 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )