Affix
view release on metacpan or search on metacpan
lib/Affix/marshal.c view on Meta::CPAN
}
else if (cat == INFIX_TYPE_POINTER || cat == INFIX_TYPE_REVERSE_TRAMPOLINE)
v = &vtbl_pointer;
else if (cat == INFIX_TYPE_VOID)
v = &vtbl_void;
else if (is_string_array(aTHX_ type, &is_wide)) {
if (is_wide) {
v = &wstring_vtable;
}
else {
/* Resolve the element type to check signedness */
const infix_type * el = resolve_type(aTHX_ type->meta.array_info.element_type);
if (el->meta.primitive_id == INFIX_PRIMITIVE_UINT8)
v = &vtbl_buffer; /* Array[UInt8] -> Binary */
else
v = &string_vtable; /* Array[Char] -> String */
}
}
else
v = &vtbl_lazy_aggregate;
sv_magicext(sv, owner, PERL_MAGIC_ext, v, (char *)&m, sizeof(Affix_Pin_2_Point_Oh));
SvMAGICAL_on(sv);
SvGMAGICAL_on(sv);
SvSMAGICAL_on(sv);
/* FIX 3: Never prime a lazy aggregate placeholder; let it vivify on first use. */
if (prime && v != &vtbl_lazy_aggregate && v != &vtbl_void)
v->svt_get(aTHX_ sv, mg_find(sv, PERL_MAGIC_ext));
}
#define LAZY_ARRAY_THRESHOLD 100
/**
* @brief Internal helper to build the actual Perl Hash/Array structure.
* @param ptr The raw C memory address (may be nullptr).
* @param type The infix_type definition.
* @param owner The root SV lifeline.
* @param arena The local arena for anonymous types (if any).
* @param eager If false, large arrays return a magical scalar placeholder.
* @param readonly If true, mutations via the resulting aggregate are blocked.
*/
static SV * _bind_aggregate_internal(
pTHX_ void * ptr, const infix_type * type, SV * owner, infix_arena_t * arena, bool eager, bool readonly) {
const infix_type * res = resolve_type(aTHX_ type);
infix_type_category cat = infix_type_get_category(res);
if (cat == INFIX_TYPE_STRUCT || cat == INFIX_TYPE_UNION) {
HV * hv = newHV();
Affix_Pin_2_Point_Oh m = {.ptr = ptr, .type = type, .arena = arena, .readonly = readonly};
sv_magicext((SV *)hv, owner, PERL_MAGIC_ext, &vtbl_lazy_aggregate, (char *)&m, sizeof(Affix_Pin_2_Point_Oh));
size_t count = infix_type_get_member_count(res);
for (size_t i = 0; i < count; i++) {
const infix_struct_member * m = infix_type_get_member(res, i);
SV * v = newSV(0);
/* Propagate nullptr safely so getters don't read from 0x0 + offset */
void * child_ptr = ptr ? ((char *)ptr + m->offset) : nullptr;
// Don't read the memory now. Wait until the user accesses the hash key.
// NOTE: member pins borrow the *external* lifeline (`owner`), never the
// freshly created parent HV. Referencing the parent here creates a strong
// reference cycle (HV owns the members, each member's mg_obj owns the HV)
// that Perl's refcounting cannot collect, leaking the whole pin tree and
// its arena on every bind/cast.
bind_placeholder(
aTHX_ v, child_ptr, m->type, m->bit_offset, m->bit_width, false, owner, NULL, readonly, false);
hv_store(hv, m->name ? m->name : "", strlen(m->name ? m->name : ""), v, 0);
}
return newRV_noinc((SV *)hv);
}
else if (cat == INFIX_TYPE_ARRAY || cat == INFIX_TYPE_VECTOR || cat == INFIX_TYPE_COMPLEX) {
if (is_string_array(aTHX_ type, nullptr)) {
SV * sv = newSV(0);
bind_placeholder(aTHX_ sv, ptr, type, 0, 0, true, owner, arena, readonly, false);
/* WRAP IN REFERENCE to ensure magic persists across assignments */
return newRV_noinc(sv);
}
size_t n, step;
const infix_type * el_type;
if (cat == INFIX_TYPE_COMPLEX) {
n = 2;
el_type = res->meta.complex_info.base_type;
step = el_type->size;
}
else if (cat == INFIX_TYPE_ARRAY) {
n = res->meta.array_info.num_elements;
el_type = res->meta.array_info.element_type;
step = resolve_type(aTHX_ el_type)->size;
}
else {
n = res->meta.vector_info.num_elements;
el_type = res->meta.vector_info.element_type;
step = el_type->size;
}
if (!eager && n > LAZY_ARRAY_THRESHOLD) {
SV * lazy_placeholder = newSV(0);
bind_placeholder(aTHX_ lazy_placeholder, ptr, type, 0, 0, false, owner, arena, readonly, false);
return lazy_placeholder;
}
AV * av = newAV();
Affix_Pin_2_Point_Oh am = {.ptr = ptr, .type = type, .arena = arena, .readonly = readonly};
sv_magicext((SV *)av, owner, PERL_MAGIC_ext, &vtbl_array, (char *)&am, sizeof(Affix_Pin_2_Point_Oh));
for (size_t i = 0; i < n; i++) {
SV * el = newSV(0);
void * child_ptr = ptr ? ((char *)ptr + (i * step)) : nullptr;
// Borrow the external lifeline (see struct branch above): referencing the
// parent AV here creates an uncollectable refcount cycle.
bind_placeholder(aTHX_ el, child_ptr, el_type, 0, 0, false, owner, nullptr, readonly, false);
av_push(av, el);
}
return newRV_noinc((SV *)av);
}
return newSV(0);
}
/**
* @brief Recursively constructs a Perl tree (Hash/Array) mapping to a C aggregate.
* @param ptr Pointer to the raw C memory.
* @param type The aggregate (struct/union/array) type.
* @param owner The "root" SV that owns the C memory (lifeline).
* @return A new reference to the constructed Perl aggregate.
*/
SV * bind_aggregate(pTHX_ void * ptr, const infix_type * type, SV * owner, bool readonly) {
return _bind_aggregate_internal(aTHX_ ptr, type, owner, nullptr, false, readonly);
}
/**
* @brief Public Strategy: Used by cast() to handle anonymous type lifecycle.
*/
SV * bind_aggregate_anon(pTHX_ void * ptr, const infix_type * type, SV * owner, infix_arena_t * arena, bool readonly) {
return _bind_aggregate_internal(aTHX_ ptr, type, owner, arena, false, readonly);
}
/**
* @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.
( run in 1.077 second using v1.01-cache-2.11-cpan-364913b4093 )