Affix
view release on metacpan or search on metacpan
lib/Affix.c view on Meta::CPAN
warn("Affix::free called on an unmanaged pointer");
XSRETURN_NO;
}
XS_INTERNAL(Affix_cast) {
dXSARGS;
dMY_CXT;
if (items != 2)
croak_xs_usage(cv, "pointer_or_address, new_type_signature");
/* Extract the raw address from input (Handle, Pin, or Integer) */
SV * arg = ST(0);
void * ptr_val = get_address_v2(aTHX_ arg);
if (!ptr_val)
XSRETURN_UNDEF;
/* Resolve the signature string */
SV * type_sv = ST(1);
const char * signature = _get_string_from_type_obj(aTHX_ type_sv);
if (!signature)
signature = SvPV_nolen(type_sv);
/* Determine Return Strategy: Value (Copy) vs Reference (Pin) */
bool return_as_value = false;
bool is_string_type = false;
// Check if the type object is a Const wrapper
bool readonly = _is_const_obj(aTHX_ type_sv);
/* Resolve the type object and create an arena if it's a dynamic signature */
infix_type * new_type = nullptr;
infix_arena_t * parse_arena = nullptr;
if (infix_type_from_signature(&new_type, &parse_arena, signature, MY_CXT.registry) != INFIX_SUCCESS) {
SV * err_sv = _format_parse_error(aTHX_ "for cast", signature, infix_get_last_error());
warn_sv(err_sv);
if (parse_arena)
infix_arena_destroy(parse_arena);
XSRETURN_UNDEF;
}
const infix_type * resolved = resolve_type(aTHX_ new_type);
if (resolved->category == INFIX_TYPE_PRIMITIVE || resolved->category == INFIX_TYPE_ENUM) {
return_as_value = true;
}
else if (resolved->category == INFIX_TYPE_POINTER) {
const infix_type * pointee = resolve_type(aTHX_ resolved->meta.pointer_info.pointee_type);
/* char* or uchar* are returned as Perl strings immediately */
if (pointee->category == INFIX_TYPE_PRIMITIVE &&
(pointee->meta.primitive_id == INFIX_PRIMITIVE_SINT8 ||
pointee->meta.primitive_id == INFIX_PRIMITIVE_UINT8)) {
return_as_value = true;
is_string_type = true;
}
}
/* Determine the owner lifeline */
/* This ensures that if we cast memory inside an Affix::Memory block,
the block stays alive as long as this casted variable exists. */
SV * owner = _borrow_lifeline(aTHX_ arg);
/* Execution */
if (return_as_value) {
SV * ret_val = sv_newmortal();
if (is_string_type) // String pullers expect char**, so we pass the address of our pointer
ptr2sv(aTHX_ nullptr, &ptr_val, ret_val, new_type, readonly);
else // Primitives expect the address of the data
ptr2sv(aTHX_ nullptr, ptr_val, ret_val, new_type, readonly);
/* If we parsed an anonymous signature, we can destroy the arena now
since we copied the data out into a standard Perl scalar. */
if (parse_arena)
infix_arena_destroy(parse_arena);
ST(0) = ret_val;
}
else {
/* Reference path: Create a magic-bound variable pointing to the memory */
SV * ret_val;
infix_type_category cat = resolved->category;
if (cat == INFIX_TYPE_STRUCT || cat == INFIX_TYPE_UNION || cat == INFIX_TYPE_ARRAY ||
cat == INFIX_TYPE_VECTOR || cat == INFIX_TYPE_COMPLEX) {
/* Aggregate binder handles the arena lifecycle automatically.
If parse_arena is provided, it is freed when the Perl SV is destroyed. */
ret_val = bind_aggregate_anon(aTHX_ ptr_val, new_type, owner, parse_arena, readonly);
ST(0) = sv_2mortal(ret_val);
}
else {
/* Fallback for generic pointers */
ret_val = newSV(0);
const infix_type * bind_type = new_type;
/* If it's a pointer, we typically want to bind to the pointee
so that $$var works correctly. */
if (new_type->category == INFIX_TYPE_POINTER)
bind_type = _unwrap_pin_type(new_type);
bind_placeholder(aTHX_ ret_val, ptr_val, bind_type, 0, 0, true, owner, parse_arena, readonly, true);
ST(0) = sv_2mortal(newRV_noinc(ret_val));
}
}
XSRETURN(1);
}
XS_INTERNAL(Affix_attach_destructor) {
dXSARGS;
if (items < 2)
croak_xs_usage(cv, "pin, destructor_ptr, [lib_obj]");
Affix_Pin_2_Point_Oh * pin = get_pin_v2(aTHX_ ST(0));
if (!pin) {
warn("First argument to attach_destructor must be a pinned pointer");
XSRETURN_UNDEF;
}
void * destructor_ptr = nullptr;
( run in 2.144 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )