Affix
view release on metacpan or search on metacpan
Allocates zero-initialized memory for `$count` elements of `$size`. Returns a `Pointer[Void]` pin.
```perl
my $ptr = calloc( 10, sizeof(Int) );
my $arr = cast( $ptr, Array[Int, 10] );
```
### `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
if (!_layout_struct(type)) {
*out_type = nullptr;
return INFIX_ERROR_INVALID_ARGUMENT;
}
*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].opcode = get_opcode_for_type(aTHX_ 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);
if (!pointee_name && pointee->category == INFIX_TYPE_NAMED_REFERENCE)
pointee_name = pointee->meta.named_reference.name;
// 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, $size )>
Allocates zero-initialized memory for C<$count> elements of C<$size>. Returns a C<Pointer[Void]> pin.
my $ptr = calloc( 10, sizeof(Int) );
my $arr = cast( $ptr, Array[Int, 10] );
=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
# But $r_ptr still thinks it's [2:int]. We must cast to update the type view.
my $arr_ptr = cast( $r_ptr, Array [ Int, 8 ] );
# Initialize new memory to zero
memset( $arr_ptr, 0, 32 );
# Modify perl's copy (writes directly to C memory)
$arr_ptr->[0] = 10;
$arr_ptr->[7] = 80;
# 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();
t/035_magic_struct.t view on Meta::CPAN
# BIND the hash to the pointer (The new system)
my $struct = cast $ptr, Point();
# ACT: Modify the hash
$struct->{x} = 123;
$struct->{y} = 456;
# ASSERT: Read raw memory back using cast (bypassing the hash magic)
my $raw_x = cast $ptr, Int;
my $raw_y = cast ptr_add( $ptr, 4 ), Int; # offset of 'y' is 4
is $raw_x, 123, 'C memory for x updated immediately';
is $raw_y, 456, 'C memory for y updated immediately';
};
subtest 'Reference counting and persistence' => sub {
my $sub_hash;
{
my $root_ptr = Affix::malloc( sizeof( Transform() ) );
my $tx = cast $root_ptr, Transform();
# Grab a reference to a nested struct
$sub_hash = $tx->{origin};
}
t/081_packed.t view on Meta::CPAN
is $p->{b}, 54321, 'Write and read packed field b';
};
#
subtest 'Packed char + uint64_t -- C-level roundtrip' => sub {
my $mem = calloc( 1, sizeof_packed_char_u64() );
fill_packed_u64($mem);
is get_packed_u64_char($mem), 88, 'C reads packed char as 88 (ASCII "X")';
is get_packed_u64_val($mem), 0xDEADBEEFCAFEBABE, 'C reads packed uint64_t correctly';
my $p = cast( $mem, Packed( Struct [ a => Char, b => UInt64 ] ) );
$p->{a} = 90;
is get_packed_u64_char($mem), 90, 'C sees char updated via magic';
$p->{b} = 0x1122334455667788;
is get_packed_u64_val($mem), 0x1122334455667788, 'C sees u64 updated via magic';
};
#
subtest 'Packed struct -- by-value return' => sub {
my $ret = make_packed_abc( 42, -66, 99 );
is ref($ret), 'HASH', 'Return value is a hashref';
is $ret->{a}, 42, 'Field a returned correctly';
is $ret->{b}, -66, 'Field b returned correctly';
is $ret->{c}, 99, 'Field c returned correctly';
};
#
t/083_pin_conventions.t view on Meta::CPAN
# Write through clone, read through original
$cloned = 77;
is $original, 77, 'Original sees writes through clone';
# Write through original, read through clone
$original = 88;
is $cloned, 88, 'Clone sees writes through original';
# Both update C
is $get->(), 88, 'C global updated through either pin';
ok unpin($cloned), 'Unpinned clone';
ok unpin($original), 'Unpinned original';
};
#
subtest 'unpin() returns false for non-pinned scalar' => sub {
ok !unpin(42), 'unpin returns false for plain integer';
ok !unpin("hello"), 'unpin returns false for string';
};
#
done_testing;
( run in 1.592 second using v1.01-cache-2.11-cpan-bbc515a03b3 )