Affix
view release on metacpan or search on metacpan
lib/Affix/marshal.c view on Meta::CPAN
/**
* @brief Registers multiple types into the global Infix Type Registry.
* @param defs The Infix type definition string.
*/
void define_types(pTHX_ const char * defs) {
dMY_CXT;
if (infix_register_types(MY_CXT.registry, defs) != INFIX_SUCCESS)
croak("Parse Error");
}
/**
* @brief Returns the layout size of a type by name.
* @param name Type name or AST signature.
* @return Size in bytes.
*/
IV sizeof_type(pTHX_ const char * name) {
dMY_CXT;
const infix_type * t = infix_registry_lookup_type(MY_CXT.registry, name);
if (!t) {
infix_arena_t * ta;
infix_type * tt;
if (infix_type_from_signature(&tt, &ta, name, MY_CXT.registry) == INFIX_SUCCESS) {
size_t s = tt->size;
infix_arena_destroy(ta);
return s;
}
}
return t ? t->size : 0;
}
/**
* @brief Returns the byte offset of a member in a struct/union.
* @param type_name The aggregate type name.
* @param member_name The member field name.
* @return Offset in bytes, or -1 if not found.
*/
IV offsetof_member(pTHX_ const char * type_name, const char * member_name) {
dMY_CXT;
const infix_type * t = resolve_type(aTHX_ infix_registry_lookup_type(MY_CXT.registry, type_name));
if (!t || (t->category != INFIX_TYPE_STRUCT && t->category != INFIX_TYPE_UNION))
return -1;
for (size_t i = 0; i < t->meta.aggregate_info.num_members; i++)
if (strEQ(t->meta.aggregate_info.members[i].name, member_name))
return (IV)t->meta.aggregate_info.members[i].offset;
return -1;
}
/**
* @brief Casts a raw pointer (or memory block) to a magic-bound Perl variable mapping its layout.
* @param in The input SV (integer address or Affix::Memory managed object).
* @param name The struct or primitive type name to cast the memory into.
* @return A magic-bound SV tracking the memory block natively.
*/
SV * cast(pTHX_ SV * in, const char * name) {
dMY_CXT;
void * addr = get_address_v2(aTHX_ in);
if (!addr)
return &PL_sv_undef;
/* Keep the blessed Affix::Memory object itself as the lifeline so the pin
holds a strong reference to it. This keeps the memory alive for as long as
any derived pin exists and lets free()/DESTROY locate the owner. */
SV * owner = (SvROK(in) && sv_derived_from(in, "Affix::Memory")) ? in : nullptr;
infix_type * new_type = nullptr;
infix_arena_t * local_arena = nullptr;
if (infix_type_from_signature(&new_type, &local_arena, name, MY_CXT.registry) != INFIX_SUCCESS)
croak("Type not found: %s", name);
const infix_type * resolved = resolve_type(aTHX_ new_type);
infix_type_category cat = infix_type_get_category(resolved);
/* AGGREGATES: Return a Reference (HashRef, ArrayRef, or ScalarRef for strings) */
if (cat == INFIX_TYPE_STRUCT || cat == INFIX_TYPE_UNION || cat == INFIX_TYPE_ARRAY || cat == INFIX_TYPE_VECTOR ||
cat == INFIX_TYPE_COMPLEX) {
return bind_aggregate_anon(aTHX_ addr, new_type, owner, local_arena, false);
}
/* PRIMITIVES: Return a scalar reference ($$ptr) to allow writes back to C */
SV * sv = newSV(0);
bind_placeholder(aTHX_ sv, addr, new_type, 0, 0, true, owner, local_arena, false, true);
return newRV_noinc(sv);
}
/**
* @brief Wraps an existing C pointer with a custom destructor callback into an object payload.
* @param ptr_iv The raw pointer (as integer).
* @param dtor_iv The destructor callback pointer (as integer).
* @return An Affix::Memory object representing the mapped block.
*/
SV * wrap_owned(pTHX_ UV ptr_uv, UV dtor_uv) {
AV * av = newAV();
av_push(av, newSVuv(ptr_uv)); /* Use UV */
av_push(av, newSVuv(dtor_uv)); /* Use UV */
SV * rv = newRV_noinc((SV *)av);
sv_bless(rv, gv_stashpv("Affix::Memory", GV_ADD));
return rv;
}
/**
* @brief Allocates zeroed C memory and wraps it into a Perl Affix::Memory object.
* @param size Memory allocation size in bytes.
* @return An Affix::Memory object representation.
*/
SV * alloc_owned(pTHX_ UV size) {
void * ptr = safecalloc(1, size);
SV * sv = newSVuv(PTR2UV(ptr)); /* Use UV */
SV * rv = newRV_noinc(sv);
sv_bless(rv, gv_stashpv("Affix::Memory", GV_ADD));
return rv;
}
/**
* @brief Garbage Collector Hook: Frees C memory owned by an Affix::Memory object.
* @details Can fall back to standard `safefree` or use a custom C++ destructor mapping if passed via `wrap_owned`.
* @param rv The Affix::Memory reference triggered by DESTROY.
*/
void free_owned(pTHX_ SV * rv) {
if (!rv || !SvROK(rv))
return;
SV * sv = SvRV(rv);
if (SvTYPE(sv) == SVt_PVAV) {
AV * av = (AV *)sv;
SV ** r_ptr = av_fetch(av, 0, 0);
SV ** r_dtor = av_fetch(av, 1, 0);
if (r_ptr && *r_ptr && SvOK(*r_ptr)) {
/* Use UV directly to bypass INT2PTR sign-extension bugs */
UV raw_ptr = SvUV(*r_ptr);
if (raw_ptr) {
/* Zero the pointer immediately to prevent double-free or recursion */
sv_setuv(*r_ptr, 0);
if (r_dtor && *r_dtor && SvOK(*r_dtor)) {
UV raw_dtor = SvUV(*r_dtor);
if (raw_dtor) {
/* Explicit cast to function pointer */
typedef void (*dtor_t)(void *);
dtor_t custom_dtor = (dtor_t)raw_dtor;
custom_dtor((void *)raw_ptr);
return;
}
}
safefree((void *)raw_ptr);
}
}
}
/* Case 2: alloc_owned() stores ptr directly in a UV scalar */
else if (SvOK(sv)) {
UV raw_ptr = SvUV(sv);
if (raw_ptr) {
sv_setuv(sv, 0);
safefree((void *)raw_ptr);
}
}
}
IV alloc_raw(pTHX_ IV sz) { return PTR2IV(safecalloc(1, sz)); }
void set_mem_u128(IV addr, IV l, IV h) {
unsigned __int128 * p = (unsigned __int128 *)addr;
*p = ((unsigned __int128)h << 64) | (unsigned __int128)l;
}
IV get_string_ptr() {
static char * m = "Hello from C Pointer";
return PTR2IV(m);
}
lib/Affix/marshal.c view on Meta::CPAN
}
XSRETURN(1);
}
XS_INTERNAL(XS_main_mock_cxx_delete) {
dVAR;
dXSARGS;
if (items != 1)
croak_xs_usage(cv, "ptr");
mock_cxx_delete(INT2PTR(void *, SvIV(ST(0))));
XSRETURN_EMPTY;
}
XS_INTERNAL(XS_main_get_mock_cxx_dtor_calls) {
dVAR;
dXSARGS;
if (items != 0)
croak_xs_usage(cv, "");
{
int RETVAL;
dXSTARG;
RETVAL = get_mock_cxx_dtor_calls();
TARGi((IV)RETVAL, 1);
ST(0) = TARG;
}
XSRETURN(1);
}
/**
* @brief Helper to verify if a VTable belongs to the Affix system (v1 or v2).
*/
int is_v2_vtable(MGVTBL * v) {
if (!v)
return 0;
return (v == &vtbl_sint8 || v == &vtbl_uint8 || v == &vtbl_sint16 || v == &vtbl_uint16 || v == &vtbl_sint32 ||
v == &vtbl_uint32 || v == &vtbl_sint64 || v == &vtbl_uint64 || v == &vtbl_sint128 || v == &vtbl_uint128 ||
v == &vtbl_float || v == &vtbl_double || v == &vtbl_float16 || v == &vtbl_bool || v == &vtbl_void ||
v == &vtbl_bitfield || v == &vtbl_pointer || v == &vtbl_array || v == &string_vtable ||
v == &wstring_vtable || v == &vtbl_lazy_aggregate || v == &vtbl_enum || v == &vtbl_buffer);
}
/**
* @brief Internal helper to safely extract a pointer address from a Perl scalar.
* @details This function handles Affix::Memory objects, v2.0 magical Pins, and
* raw integers. It includes logic to handle Pointer[Void] correctly by
* suppressing the dereference that is normally required for typed pointers.
*
* @param sv The Perl Scalar to inspect.
* @param ignore_mg A specific magic pointer to skip (prevents self-extraction during assignment).
* @return The raw C pointer address, or NULL if extraction fails.
*/
void * _extract_pointer_value(pTHX_ SV * sv, MAGIC * ignore_mg) {
if (!sv)
return nullptr;
// Use a secondary pointer for unwrapping to preserve the original SV (the potential object)
SV * target = sv;
if (SvROK(sv))
target = SvRV(sv);
/* Handle Magic Pins (even inside blessed objects) */
if (SvMAGICAL(target)) {
MAGIC * mg = mg_find(target, PERL_MAGIC_ext);
while (mg) {
if (is_v2_vtable(mg->mg_virtual) && mg != ignore_mg) {
Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;
if (mg->mg_virtual == &vtbl_pointer)
return im->absolute ? im->ptr : (im->ptr ? *(void **)im->ptr : nullptr);
return im->ptr;
}
mg = mg->mg_moremagic;
}
}
/* Handle Affix::Memory Handles */
if (sv_isobject(sv) && sv_derived_from(sv, "Affix::Memory")) {
SV * rv = SvRV(sv);
if (SvTYPE(rv) == SVt_PVAV) {
SV ** p = av_fetch((AV *)rv, 0, 0);
return (p && *p) ? INT2PTR(void *, SvUV(*p)) : nullptr;
}
return INT2PTR(void *, SvUV(rv));
}
/* Fallback: Raw Integers */
if (SvIOK(sv))
return INT2PTR(void *, SvUV(sv));
return nullptr;
}
/**
* @brief The internal C function to extract a pointer from the 2.0 memory system.
* @param sv The SV to inspect (Handle or Magical Variable).
* @return The raw C pointer, or nullptr if not found.
*/
void * get_address_v2(pTHX_ SV * sv) { return _extract_pointer_value(aTHX_ sv, nullptr); }
/**
* @brief Determines the underlying type for a pointer pin.
* @details Prevents unwrapping char* (which should remain a string) but unwraps others
* so that $$ptr reads the correct data type.
*/
const infix_type * _unwrap_pin_type(const infix_type * type) {
if (type->category == INFIX_TYPE_POINTER) {
const infix_type * pointee = type->meta.pointer_info.pointee_type;
/* Do not unwrap char*; it's handled as a terminal String */
if (pointee->category == INFIX_TYPE_PRIMITIVE && (pointee->meta.primitive_id <= INFIX_PRIMITIVE_UINT8))
return type;
/* Do not unwrap Void; we want Pointer[Void] pins for raw addresses */
if (pointee->category == INFIX_TYPE_VOID)
return type;
return pointee;
}
return type;
}
/**
* @brief Checks if a Perl Scalar is managed by the new 2.0 memory system.
* @details This identifies SVs that are directly mapped to C memory via the
* Affix_Pin_2_Point_Oh struct and its associated VTables or an Affix::Memory handle.
( run in 0.703 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )