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