Affix
view release on metacpan or search on metacpan
lib/Affix.c view on Meta::CPAN
case OP_RET_BOOL: \
sv_setbool(TARG, *(bool *)ret_buffer); \
break; \
case OP_RET_SINT8: \
sv_setiv(TARG, *(int8_t *)ret_buffer); \
break; \
case OP_RET_UINT8: \
sv_setuv(TARG, *(uint8_t *)ret_buffer); \
break; \
case OP_RET_SINT16: \
sv_setiv(TARG, *(int16_t *)ret_buffer); \
break; \
case OP_RET_UINT16: \
sv_setuv(TARG, *(uint16_t *)ret_buffer); \
break; \
case OP_RET_SINT32: \
sv_setiv(TARG, *(int32_t *)ret_buffer); \
break; \
case OP_RET_UINT32: \
sv_setuv(TARG, *(uint32_t *)ret_buffer); \
break; \
case OP_RET_SINT64: \
sv_setiv(TARG, *(int64_t *)ret_buffer); \
break; \
case OP_RET_UINT64: \
sv_setuv(TARG, *(uint64_t *)ret_buffer); \
break; \
case OP_RET_SINT128: \
sv_from_int128_safe(TARG, ret_buffer); \
break; \
case OP_RET_UINT128: \
sv_from_uint128_safe(TARG, ret_buffer); \
break; \
case OP_RET_FLOAT: \
sv_setnv(TARG, (double)*(float *)ret_buffer); \
break; \
case OP_RET_FLOAT16: \
sv_setnv(TARG, (double)half_to_float(*(infix_float16_t *)ret_buffer)); \
break; \
case OP_RET_DOUBLE: \
sv_setnv(TARG, *(double *)ret_buffer); \
break; \
case OP_RET_PTR: \
{ \
void * c_ptr = *(void **)ret_buffer; \
if (c_ptr == nullptr) { \
sv_setsv(TARG, &PL_sv_undef); \
} \
else { \
Affix_Pin * pin; \
Newxz(pin, 1, Affix_Pin); \
pin->pointer = c_ptr; \
pin->type = affix->unwrapped_ret_type; \
\
SV * obj_data = newSViv(PTR2IV(pin)); \
SV * rv = newRV_noinc(obj_data); \
\
dMY_CXT; \
if (UNLIKELY(MY_CXT.stash_pointer == nullptr)) \
MY_CXT.stash_pointer = gv_stashpv("Affix::Pointer", GV_ADD); \
(void)sv_bless(rv, MY_CXT.stash_pointer); \
\
(void)sv_magicext(obj_data, nullptr, PERL_MAGIC_ext, &Affix_pin_vtbl, (char *)pin, 0); \
sv_setsv(TARG, sv_2mortal(rv)); \
} \
break; \
} \
case OP_RET_PTR_CHAR: \
{ \
char * p = *(char **)ret_buffer; \
if (p) \
sv_setpv(TARG, p); \
else \
sv_setsv(TARG, &PL_sv_undef); \
break; \
} \
case OP_RET_PTR_WCHAR: \
pull_pointer_as_wstring(aTHX_ affix, TARG, affix->ret_type, ret_buffer); \
break; \
case OP_RET_SV: \
{ \
SV * s = *(SV **)ret_buffer; \
if (s) \
sv_setsv(TARG, s); \
else \
sv_setsv(TARG, &PL_sv_undef); \
break; \
} \
case OP_RET_CUSTOM: \
default: \
if (affix->ret_pull_handler) \
affix->ret_pull_handler(aTHX_ affix, TARG, affix->ret_type, ret_buffer); \
break; \
} \
if (UNLIKELY(affix->num_out_params > 0)) { \
for (size_t i = 0; i < affix->num_out_params; ++i) { \
const OutParamInfo * info = &affix->out_param_info[i]; \
SV * arg_sv = ST(info->perl_stack_index); \
if (SvROK(arg_sv) && !is_pin(aTHX_ arg_sv)) { \
SV * rsv = SvRV(arg_sv); \
info->writer(aTHX_ affix, info, rsv, c_args[info->perl_stack_index]); \
} \
else if (!SvOK(arg_sv) && !SvREADONLY(arg_sv)) \
info->writer(aTHX_ affix, info, arg_sv, c_args[info->perl_stack_index]); \
} \
} \
\
affix->args_arena->current_offset = arena_mark; \
affix->ret_arena->current_offset = 0; \
\
ST(0) = TARG; \
XSRETURN(1); \
}
// Generate the two XSUBs
GENERATE_TRIGGER_XSUB(Affix_trigger_stack, 1)
GENERATE_TRIGGER_XSUB(Affix_trigger_arena, 0)
static void _lib_registry_inc_ref(pTHX_ infix_library_t * lib) {
dMY_CXT;
if (MY_CXT.lib_registry == nullptr)
lib/Affix.c view on Meta::CPAN
}
infix_status status =
infix_signature_parse(sig_to_parse, &parse_arena, &ret_type, &args, &num_args, &num_fixed, MY_CXT.registry);
if (clean_sig)
safefree(clean_sig);
if (status != INFIX_SUCCESS) {
safefree(backend);
if (parse_arena)
infix_arena_destroy(parse_arena);
infix_error_details_t err = infix_get_last_error();
if (err.message[0] != '\0')
warn("Failed to parse signature for affix_bundle: %s", err.message);
else
warn("Failed to parse signature for affix_bundle (Error Code: %d)", status);
XSRETURN_UNDEF;
}
infix_direct_arg_handler_t * handlers =
(infix_direct_arg_handler_t *)safecalloc(num_args, sizeof(infix_direct_arg_handler_t));
for (size_t i = 0; i < num_args; ++i)
handlers[i] = get_direct_handler_for_type(args[i].type);
status = infix_forward_create_direct(&backend->infix, signature, symbol, handlers, MY_CXT.registry);
safefree(handlers);
infix_arena_destroy(parse_arena);
if (status != INFIX_SUCCESS) {
safefree(backend);
infix_error_details_t err = infix_get_last_error();
warn("Failed to create direct trampoline: %s", err.message[0] ? err.message : "Unknown Error");
XSRETURN_UNDEF;
}
backend->cif = infix_forward_get_direct_code(backend->infix);
backend->num_args = num_args;
backend->ret_type = infix_forward_get_return_type(backend->infix);
backend->pull_handler = get_pull_handler(aTHX_ backend->ret_type);
backend->ret_opcode = get_ret_opcode_for_type(backend->ret_type);
if (!backend->pull_handler) {
infix_forward_destroy(backend->infix);
safefree(backend);
warn("Unsupported return type for affix_bundle");
XSRETURN_UNDEF;
}
backend->lib_handle = created_implicit_handle ? lib_handle_for_symbol : nullptr;
CV * cv_new =
newXSproto_portable((ix == 0 || ix == 2) ? rename_str : nullptr, Affix_trigger_backend, __FILE__, nullptr);
CvXSUBANY(cv_new).any_ptr = (void *)backend;
SV * obj = (ix == 1 || ix == 3) ? newRV_noinc(MUTABLE_SV(cv_new)) : newRV_inc(MUTABLE_SV(cv_new));
sv_bless(obj, gv_stashpv("Affix::Bundled", GV_ADD));
ST(0) = sv_2mortal(obj);
XSRETURN(1);
}
// Standard path (parse & prepare types)
infix_arena_t * parse_arena = NULL;
infix_type * ret_type = NULL;
infix_function_argument * args = NULL;
size_t num_args = 0, num_fixed = 0;
const char * sig_to_parse = signature;
char * clean_sig = nullptr;
if (strstr(signature, "+")) {
clean_sig = savepv(signature);
const char * p = signature;
char * d = clean_sig;
while (*p) {
// Strip '+' only if it precedes a signature character: * [ { ! < ( @
if (*p == '+' &&
(p[1] == '*' || p[1] == '[' || p[1] == '{' || p[1] == '!' || p[1] == '<' || p[1] == '(' || p[1] == '@'))
p++;
else
*d++ = *p++;
}
*d = '\0';
sig_to_parse = clean_sig;
}
infix_status status =
infix_signature_parse(sig_to_parse, &parse_arena, &ret_type, &args, &num_args, &num_fixed, MY_CXT.registry);
if (clean_sig)
safefree(clean_sig);
if (status != INFIX_SUCCESS) {
infix_error_details_t err = infix_get_last_error();
warn("Failed to parse signature: %s", err.message);
if (parse_arena)
infix_arena_destroy(parse_arena);
XSRETURN_UNDEF;
}
// JIT Type substitution (array decay)
// We create a separate list of types for JIT compilation where Arrays are replaced by Pointers.
// The original Array types are kept for the marshalling plan.
infix_type ** jit_arg_types = NULL;
if (num_args > 0) {
jit_arg_types = safemalloc(sizeof(infix_type *) * num_args);
for (size_t i = 0; i < num_args; ++i) {
infix_type * t = args[i].type;
if (t->category == INFIX_TYPE_ARRAY) {
// Arrays passed as arguments decay to pointers.
// We create a Pointer[Element] type in the temp arena for JIT creation.
infix_type * ptr_type = NULL;
// FIX: Check return value to satisfy nodiscard warning
if (infix_type_create_pointer_to(parse_arena, &ptr_type, t->meta.array_info.element_type) !=
INFIX_SUCCESS) {
safefree(jit_arg_types);
infix_arena_destroy(parse_arena);
croak("Failed to create pointer type for array decay");
lib/Affix.c view on Meta::CPAN
temp_out_info[out_param_count].writer = affix_array_writeback;
out_param_count++;
}
}
affix->plan[affix->num_args].opcode = OP_DONE;
affix->total_args_size = current_offset;
affix->num_out_params = out_param_count;
if (out_param_count > 0) {
affix->out_param_info = safemalloc(sizeof(OutParamInfo) * out_param_count);
memcpy(affix->out_param_info, temp_out_info, sizeof(OutParamInfo) * out_param_count);
}
else
affix->out_param_info = nullptr;
safefree(temp_out_info);
char prototype_buf[256] = {0};
for (size_t i = 0; i < affix->num_args; ++i)
strcat(prototype_buf, "$");
// Install XSUB
XSUBADDR_t trigger;
if (is_variadic)
trigger = Affix_trigger_variadic;
else
trigger = (affix->total_args_size <= 512) ? Affix_trigger_stack : Affix_trigger_arena;
CV * cv_new = newXSproto_portable(ix == 0 ? rename_str : nullptr, trigger, __FILE__, nullptr);
if (UNLIKELY(cv_new == nullptr)) {
infix_forward_destroy(affix->infix);
SvREFCNT_dec(affix->return_sv);
infix_arena_destroy(affix->args_arena);
infix_arena_destroy(affix->ret_arena);
safefree(affix->plan);
if (affix->out_param_info)
safefree(affix->out_param_info);
if (affix->c_args)
safefree(affix->c_args);
safefree(affix->sig_str);
if (affix->sym_name)
safefree(affix->sym_name);
SvREFCNT_dec(affix->variadic_cache);
safefree(affix);
warn("Failed to install new XSUB");
infix_arena_destroy(parse_arena);
XSRETURN_UNDEF;
}
// Attach magic for lifecycle management
// We MUST use nullptr/0 here and assign mg_ptr manually, otherwise sv_magicext treats 'affix' as a string and
// copies truncated garbage.
MAGIC * mg = sv_magicext((SV *)cv_new, nullptr, PERL_MAGIC_ext, &Affix_cv_vtbl, nullptr, 0);
mg->mg_ptr = (char *)affix;
// Set optimization pointer
CvXSUBANY(cv_new).any_ptr = (void *)affix;
//
SV * obj = (ix == 1 || ix == 3) ? newRV_noinc(MUTABLE_SV(cv_new)) : newRV_inc(MUTABLE_SV(cv_new));
sv_bless(obj, gv_stashpv("Affix", GV_ADD));
ST(0) = sv_2mortal(obj);
infix_arena_destroy(parse_arena); // Now safe to destroy as we deep-copied everything
XSRETURN(1);
}
XS_INTERNAL(Affix_Bundled_DESTROY) {
dXSARGS;
dMY_CXT;
PERL_UNUSED_VAR(items);
Affix_Backend * backend;
STMT_START {
HV * st;
GV * gvp;
SV * const xsub_tmp_sv = ST(0);
SvGETMAGIC(xsub_tmp_sv);
CV * cv_ptr = sv_2cv(xsub_tmp_sv, &st, &gvp, 0);
backend = (Affix_Backend *)CvXSUBANY(cv_ptr).any_ptr;
}
STMT_END;
if (backend) {
if (backend->lib_handle != nullptr && MY_CXT.lib_registry != nullptr) {
hv_iterinit(MY_CXT.lib_registry);
HE * he;
while ((he = hv_iternext(MY_CXT.lib_registry))) {
SV * entry_sv = HeVAL(he);
LibRegistryEntry * entry = INT2PTR(LibRegistryEntry *, SvIV(entry_sv));
if (entry->lib == backend->lib_handle) {
entry->ref_count--;
if (entry->ref_count == 0) {
infix_library_close(entry->lib);
safefree(entry);
hv_delete_ent(MY_CXT.lib_registry, HeKEY_sv(he), G_DISCARD, 0);
}
break;
}
}
}
if (backend->infix)
infix_forward_destroy(backend->infix);
safefree(backend);
}
XSRETURN_EMPTY;
}
XS_INTERNAL(Affix_DESTROY) {
dXSARGS;
dMY_CXT;
PERL_UNUSED_VAR(items);
Affix * affix;
STMT_START {
HV * st;
GV * gvp;
SV * const xsub_tmp_sv = ST(0);
SvGETMAGIC(xsub_tmp_sv);
CV * cv_ptr = sv_2cv(xsub_tmp_sv, &st, &gvp, 0);
affix = (Affix *)CvXSUBANY(cv_ptr).any_ptr;
}
STMT_END;
if (affix != nullptr)
_affix_destroy(aTHX_ affix);
XSRETURN_EMPTY;
}
static void pull_sint8(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setiv(sv, *(int8_t *)p); }
static void pull_uint8(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setuv(sv, *(uint8_t *)p); }
static void pull_sint16(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setiv(sv, *(int16_t *)p); }
static void pull_uint16(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setuv(sv, *(uint16_t *)p); }
static void pull_sint32(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setiv(sv, *(int32_t *)p); }
static void pull_uint32(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setuv(sv, *(uint32_t *)p); }
static void pull_sint64(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setiv(sv, *(int64_t *)p); }
static void pull_uint64(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setuv(sv, *(uint64_t *)p); }
static void pull_float16(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) {
sv_setnv(sv, (double)half_to_float(*(infix_float16_t *)p));
}
static void pull_float(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setnv(sv, *(float *)p); }
static void pull_double(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setnv(sv, *(double *)p); }
static void pull_long_double(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) {
sv_setnv(sv, *(long double *)p);
}
static void pull_bool(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setbool(sv, *(bool *)p); }
static void pull_void(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_setsv(sv, &PL_sv_undef); }
#if !defined(INFIX_COMPILER_MSVC)
static void pull_sint128(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_from_int128_safe(sv, p); }
static void pull_uint128(pTHX_ Affix * affix, SV * sv, const infix_type * t, void * p) { sv_from_uint128_safe(sv, p); }
#endif
static void pull_struct(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p) {
HV * hv;
bool live = (sv_isobject(sv) && sv_derived_from(sv, "Affix::Live")) ||
(SvROK(sv) && sv_isobject(SvRV(sv)) && sv_derived_from(SvRV(sv), "Affix::Live"));
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
hv = (HV *)SvRV(sv);
else {
hv = newHV();
sv_setsv(sv, sv_2mortal(newRV_noinc(MUTABLE_SV(hv))));
}
_populate_hv_from_c_struct(aTHX_ affix, hv, type, p, live, nullptr);
}
static void pull_union(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p) {
HV * hv;
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV) {
hv = (HV *)SvRV(sv);
hv_clear(hv);
}
else {
hv = newHV();
SV * rv = newRV_noinc(MUTABLE_SV(hv));
sv_bless(rv, gv_stashpv("Affix::Live", GV_ADD));
sv_setsv(sv, sv_2mortal(rv));
}
_populate_hv_from_c_struct(aTHX_ affix, hv, type, p, true, nullptr);
}
// Helper for portability if strnlen isn't available
static size_t _safe_strnlen(const char * s, size_t maxlen) {
size_t len;
for (len = 0; len < maxlen; len++, s++)
if (!*s)
break;
return len;
}
static void pull_array(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p) {
const infix_type * element_type = type->meta.array_info.element_type;
if (element_type->category == INFIX_TYPE_PRIMITIVE) {
if (element_type->meta.primitive_id == INFIX_PRIMITIVE_SINT8) {
// char[] / int8[]: Treat as fixed buffer but strip trailing nulls.
// This satisfies typical C-string in struct usage (padded with nulls)
// AND binary usage where nulls are embedded (as long as not trailing).
size_t len = type->meta.array_info.num_elements;
const char * ptr = (const char *)p;
while (len > 0 && ptr[len - 1] == '\0')
len--;
sv_setpvn(sv, ptr, len);
return;
}
if (element_type->meta.primitive_id == INFIX_PRIMITIVE_UINT8) {
// uchar[] / uint8[]: Treat as raw binary blob, read full length.
sv_setpvn(sv, (const char *)p, type->meta.array_info.num_elements);
return;
}
}
// Standard array handling (ArrayRef of values)
AV * av;
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) {
av = (AV *)SvRV(sv);
av_clear(av);
}
else {
av = newAV();
sv_setsv(sv, sv_2mortal(newRV_noinc(MUTABLE_SV(av))));
}
size_t num_elements = type->meta.array_info.num_elements;
size_t element_size = infix_type_get_size(element_type);
av_extend(av, num_elements);
for (size_t i = 0; i < num_elements; ++i) {
void * element_ptr = (char *)p + (i * element_size);
SV * element_sv = newSV(0);
ptr2sv(aTHX_ affix, element_ptr, element_sv, element_type);
av_push(av, element_sv);
}
}
static void pull_reverse_trampoline(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p) {
sv_setiv(sv, PTR2IV(*(void **)p));
}
lib/Affix.c view on Meta::CPAN
}
const infix_type * base_type = type->meta.complex_info.base_type;
size_t base_size = infix_type_get_size(base_type);
SV * real_sv = newSV(0);
ptr2sv(aTHX_ affix, p, real_sv, base_type);
av_push(av, real_sv);
SV * imag_sv = newSV(0);
ptr2sv(aTHX_ affix, (char *)p + base_size, imag_sv, base_type);
av_push(av, imag_sv);
}
static void pull_vector(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p) {
AV * av;
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) {
av = (AV *)SvRV(sv);
av_clear(av);
}
else {
av = newAV();
sv_setsv(sv, sv_2mortal(newRV_noinc(MUTABLE_SV(av))));
}
const infix_type * element_type = type->meta.vector_info.element_type;
size_t num_elements = type->meta.vector_info.num_elements;
size_t element_size = infix_type_get_size(element_type);
av_extend(av, num_elements);
for (size_t i = 0; i < num_elements; ++i) {
void * element_ptr = (char *)p + (i * element_size);
SV * element_sv = newSV(0);
ptr2sv(aTHX_ affix, element_ptr, element_sv, element_type);
av_push(av, element_sv);
}
}
static void pull_pointer_as_string(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
void * c_ptr = *(void **)ptr;
if (c_ptr == nullptr)
sv_setsv(sv, &PL_sv_undef);
else
sv_setpv(sv, (const char *)c_ptr);
}
static void pull_pointer_as_struct(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
void * c_ptr = *(void **)ptr;
if (c_ptr == nullptr)
sv_setsv(sv, &PL_sv_undef);
else {
const infix_type * pointee_type = type->meta.pointer_info.pointee_type;
pull_struct(aTHX_ affix, sv, pointee_type, c_ptr);
}
}
static void pull_struct_as_live(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
void * c_ptr = *(void **)ptr;
if (c_ptr == nullptr) {
sv_setsv(sv, &PL_sv_undef);
return;
}
const infix_type * pointee_type = type->meta.pointer_info.pointee_type;
HV * hv = newHV();
SV * rv = newRV_noinc(MUTABLE_SV(hv));
sv_bless(rv, gv_stashpv("Affix::Live", GV_ADD));
_populate_hv_from_c_struct(aTHX_ affix, hv, pointee_type, c_ptr, true, nullptr);
sv_setsv(sv, rv);
SvREFCNT_dec(rv);
}
static void pull_pointer_as_array(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
void * c_ptr = *(void **)ptr;
if (c_ptr == nullptr)
sv_setsv(sv, &PL_sv_undef);
else {
const infix_type * pointee_type = type->meta.pointer_info.pointee_type;
pull_array(aTHX_ affix, sv, pointee_type, c_ptr);
}
}
static void pull_pointer_as_pin(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
void * c_ptr = *(void **)ptr;
if (c_ptr == nullptr) {
sv_setsv(sv, &PL_sv_undef);
return;
}
Affix_Pin * pin;
Newxz(pin, 1, Affix_Pin);
pin->pointer = c_ptr;
// Ensure we point to the content type, not Pointer[Content]
pin->type = _unwrap_pin_type(type);
pin->managed = false;
SV * obj_data = newSV(0);
sv_setiv(obj_data, PTR2IV(pin));
// Create the Reference
SV * rv = sv_2mortal(newRV_noinc(obj_data));
// Bless into Affix::Pointer BEFORE attaching magic to avoid triggering 'set' during blessing
(void)sv_bless(rv, gv_stashpv("Affix::Pointer", GV_ADD));
MAGIC * mg = sv_magicext(obj_data, nullptr, PERL_MAGIC_ext, &Affix_pin_vtbl, nullptr, 0);
mg->mg_ptr = (char *)pin;
// Update the target SV
sv_setsv(sv, rv);
}
static void pull_sv(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
void * c_ptr = *(void **)ptr;
if (c_ptr == nullptr)
sv_setsv(sv, &PL_sv_undef);
else
sv_setsv(sv, (SV *)c_ptr);
}
static void pull_file(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * ptr) {
PERL_UNUSED_VAR(affix);
PERL_UNUSED_VAR(type);
FILE * fp = *(FILE **)ptr;
if (!fp) {
sv_setsv(sv, &PL_sv_undef);
return;
}
// Duplicate FD to avoid double-close issues
int fd =
#ifdef _WIN32
_fileno
#else
fileno
#endif
(fp);
if (fd < 0) {
sv_setsv(sv, &PL_sv_undef);
return;
}
int new_fd = PerlLIO_dup(fd);
if (new_fd < 0) {
sv_setsv(sv, &PL_sv_undef);
return;
}
PerlIO * new_pio = PerlIO_fdopen(new_fd, "r+"); // Assuming R/W safe
if (!new_pio) {
PerlLIO_close(new_fd);
sv_setsv(sv, &PL_sv_undef);
return;
}
GV * gv = newGVgen("Affix::FileHandle");
if (do_open(gv, "+<&", 3, FALSE, 0, 0, new_pio))
sv_setsv(sv, sv_2mortal(newRV((SV *)gv)));
else {
PerlIO_close(new_pio);
sv_setsv(sv, &PL_sv_undef);
}
}
lib/Affix.c view on Meta::CPAN
snippet,
pointer,
err.message,
err.position));
}
XS_INTERNAL(Affix_Lib_as_string) {
dVAR;
dXSARGS;
if (items < 1)
croak_xs_usage(cv, "$lib");
IV RETVAL;
{
infix_library_t * lib;
IV tmp = SvIV((SV *)SvRV(ST(0)));
lib = INT2PTR(infix_library_t *, tmp);
RETVAL = PTR2IV(lib->handle);
}
XSRETURN_IV(RETVAL);
};
XS_INTERNAL(Affix_Lib_DESTROY) {
dXSARGS;
dMY_CXT;
if (items != 1)
croak_xs_usage(cv, "$lib");
IV tmp = SvIV((SV *)SvRV(ST(0)));
infix_library_t * lib = INT2PTR(infix_library_t *, tmp);
if (MY_CXT.lib_registry) {
hv_iterinit(MY_CXT.lib_registry);
HE * he;
SV * key_to_delete = nullptr;
while ((he = hv_iternext(MY_CXT.lib_registry))) {
SV * entry_sv = HeVAL(he);
LibRegistryEntry * entry = INT2PTR(LibRegistryEntry *, SvIV(entry_sv));
if (entry->lib == lib) {
entry->ref_count--;
if (entry->ref_count == 0) {
key_to_delete = sv_2mortal(newSVsv(HeKEY_sv(he)));
infix_library_close(entry->lib);
safefree(entry);
}
break;
}
}
if (key_to_delete)
hv_delete_ent(MY_CXT.lib_registry, key_to_delete, G_DISCARD, 0);
}
XSRETURN_EMPTY;
}
XS_INTERNAL(Affix_load_library) {
dXSARGS;
dMY_CXT;
if (items != 1)
croak_xs_usage(cv, "library_path");
const char * path = SvPV_nolen(ST(0));
SV ** entry_sv_ptr = hv_fetch(MY_CXT.lib_registry, path, strlen(path), 0);
if (entry_sv_ptr) {
LibRegistryEntry * entry = INT2PTR(LibRegistryEntry *, SvIV(*entry_sv_ptr));
entry->ref_count++;
SV * obj_data = newSV(0);
sv_setiv(obj_data, PTR2IV(entry->lib));
ST(0) = sv_2mortal(sv_bless(newRV_inc(obj_data), gv_stashpv("Affix::Lib", GV_ADD)));
XSRETURN(1);
}
infix_library_t * lib = infix_library_open(path);
if (lib) {
LibRegistryEntry * new_entry;
Newxz(new_entry, 1, LibRegistryEntry);
new_entry->lib = lib;
new_entry->ref_count = 1;
hv_store(MY_CXT.lib_registry, path, strlen(path), newSViv(PTR2IV(new_entry)), 0);
SV * obj_data = newSV(0);
sv_setiv(obj_data, PTR2IV(lib));
ST(0) = sv_2mortal(sv_bless(newRV_inc(obj_data), gv_stashpv("Affix::Lib", GV_ADD)));
XSRETURN(1);
}
XSRETURN_UNDEF;
}
XS_INTERNAL(Affix_get_last_error_message) {
dXSARGS;
PERL_UNUSED_VAR(items);
infix_error_details_t err = infix_get_last_error();
if (err.message[0] != '\0')
ST(0) = sv_2mortal(newSVpv(err.message, 0));
#if defined(INFIX_OS_WINDOWS)
else if (err.system_error_code != 0) {
char buf[256];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
err.system_error_code,
0,
buf,
sizeof(buf),
nullptr);
ST(0) = sv_2mortal(newSVpvf("System error: %s (code %ld)", buf, err.system_error_code));
}
#endif
else
ST(0) = sv_2mortal(newSVpvf("Infix error code %d at position %zu", (int)err.code, err.position));
XSRETURN(1);
}
Affix_Pin * _get_pin_from_sv(pTHX_ SV * sv) {
if (!sv || !SvOK(sv) || !SvROK(sv) || !SvMAGICAL(SvRV(sv)))
return nullptr;
MAGIC * mg = mg_findext(SvRV(sv), PERL_MAGIC_ext, &Affix_pin_vtbl);
if (mg)
return (Affix_Pin *)mg->mg_ptr;
return nullptr;
}
static int Affix_set_pin(pTHX_ SV * sv, MAGIC * mg) {
Affix_Pin * pin = (Affix_Pin *)mg->mg_ptr;
if (!pin || !pin->pointer || !pin->type)
return 0;
if (pin->bit_width > 0) {
size_t sz = infix_type_get_size(pin->type);
uint64_t val = (uint64_t)SvUV(sv);
uint64_t mask = ((uint64_t)1 << pin->bit_width) - 1;
val &= mask;
uint64_t current = 0;
memcpy(¤t, pin->pointer, sz);
current &= ~(mask << pin->bit_offset);
current |= (val << pin->bit_offset);
memcpy(pin->pointer, ¤t, sz);
return 0;
}
const infix_type * type_to_marshal = pin->type;
if (pin->type->category == INFIX_TYPE_POINTER) {
const infix_type * pointee = pin->type->meta.pointer_info.pointee_type;
if (pointee->category == INFIX_TYPE_VOID) {
if (pin->size > 0) {
lib/Affix.c view on Meta::CPAN
pin->destructor(pin->pointer);
else if (pin->managed && pin->pointer)
safefree(pin->pointer);
if (pin->destructor_lib_sv) {
if (!PL_dirty)
SvREFCNT_dec(pin->destructor_lib_sv);
}
if (pin->owner_sv) {
if (!PL_dirty)
SvREFCNT_dec(pin->owner_sv);
}
if (pin->type_arena != nullptr)
infix_arena_destroy(pin->type_arena);
safefree(pin);
mg->mg_ptr = nullptr;
return 0;
}
static int Affix_get_pin(pTHX_ SV * sv, MAGIC * mg) {
Affix_Pin * pin = (Affix_Pin *)mg->mg_ptr;
if (!pin || !pin->pointer) {
sv_setsv_mg(sv, &PL_sv_undef);
return 0;
}
if (pin->bit_width > 0) {
size_t sz = infix_type_get_size(pin->type);
uint64_t raw = 0;
memcpy(&raw, pin->pointer, sz);
uint64_t val = (raw >> pin->bit_offset) & (((uint64_t)1 << pin->bit_width) - 1);
sv_setuv(sv, val);
return 0;
}
if (pin->type && pin->type->category == INFIX_TYPE_POINTER) {
const infix_type * pointee = pin->type->meta.pointer_info.pointee_type;
if (pointee->category == INFIX_TYPE_PRIMITIVE &&
(pointee->meta.primitive_id == INFIX_PRIMITIVE_SINT8 ||
pointee->meta.primitive_id == INFIX_PRIMITIVE_UINT8)) {
const char * str = (const char *)pin->pointer;
if (str)
sv_setpv(sv, str);
else
sv_setsv(sv, &PL_sv_undef);
return 0;
}
if (pointee->category == INFIX_TYPE_VOID) {
sv_setuv(sv, PTR2UV(pin->pointer));
return 0;
}
if (pointee->category == INFIX_TYPE_STRUCT || pointee->category == INFIX_TYPE_UNION) {
HV * hv = newHV();
SV * rv = newRV_noinc(MUTABLE_SV(hv));
sv_bless(rv, gv_stashpv("Affix::Live", GV_ADD));
_populate_hv_from_c_struct(
aTHX_ nullptr, hv, pointee, pin->pointer, true, pin->owner_sv ? pin->owner_sv : sv);
sv_setsv(sv, rv);
SvREFCNT_dec(rv);
return 0;
}
if (pointee->category == INFIX_TYPE_ARRAY) {
Affix_Pin * new_pin;
Newxz(new_pin, 1, Affix_Pin);
new_pin->pointer = pin->pointer;
new_pin->managed = false;
new_pin->owner_sv = pin->owner_sv ? pin->owner_sv : sv;
SvREFCNT_inc(new_pin->owner_sv);
new_pin->type_arena = infix_arena_create(256);
new_pin->type = _copy_type_graph_to_arena(new_pin->type_arena, pointee);
sv_setsv(sv, sv_2mortal(_new_pointer_obj(aTHX_ new_pin)));
return 0;
}
}
if (pin->type)
ptr2sv(aTHX_ nullptr, pin->pointer, sv, pin->type);
return 0;
}
bool is_pin(pTHX_ SV * sv) {
if (!sv || !SvOK(sv) || !SvROK(sv) || !SvMAGICAL(SvRV(sv)))
return false;
return mg_findext(SvRV(sv), PERL_MAGIC_ext, &Affix_pin_vtbl) != nullptr;
}
void _pin_sv(pTHX_ SV * sv,
const infix_type * type,
void * pointer,
bool managed,
SV * owner_sv,
size_t bit_offset,
size_t bit_width) {
if (SvREADONLY(sv))
return;
SvUPGRADE(sv, SVt_PVMG);
MAGIC * mg = mg_findext(sv, PERL_MAGIC_ext, &Affix_pin_vtbl);
Affix_Pin * pin;
if (mg) {
pin = (Affix_Pin *)mg->mg_ptr;
if (pin && pin->managed && pin->pointer)
safefree(pin->pointer);
if (pin && pin->type_arena) {
infix_arena_destroy(pin->type_arena);
pin->type_arena = nullptr;
}
if (pin && pin->owner_sv) {
SvREFCNT_dec(pin->owner_sv);
pin->owner_sv = nullptr;
}
}
else {
Newxz(pin, 1, Affix_Pin);
mg = sv_magicext(sv, nullptr, PERL_MAGIC_ext, &Affix_pin_vtbl, nullptr, 0);
}
// Re-assign mg_ptr because sv_magicext(..., 0) likely corrupted it by treating pin as a string
mg->mg_ptr = (char *)pin;
pin->pointer = pointer;
pin->managed = managed;
pin->bit_offset = bit_offset;
pin->bit_width = bit_width;
if (owner_sv) {
pin->owner_sv = owner_sv;
SvREFCNT_inc(pin->owner_sv);
}
pin->type_arena = infix_arena_create(2048);
if (!pin->type_arena) {
safefree(pin);
mg->mg_ptr = nullptr;
croak("Failed to create memory arenas for pin's type information");
}
pin->type = _copy_type_graph_to_arena(pin->type_arena, type);
if (!pin->type) {
infix_arena_destroy(pin->type_arena);
safefree(pin);
mg->mg_ptr = nullptr;
croak("Failed to copy type information into pin");
}
}
XS_INTERNAL(Affix_find_symbol) {
dXSARGS;
dMY_CXT; // Require the thread-local context
if (items != 2 || !sv_isobject(ST(0)) || !sv_derived_from(ST(0), "Affix::Lib"))
croak_xs_usage(cv, "Affix_Lib_object, symbol_name");
IV tmp = SvIV((SV *)SvRV(ST(0)));
infix_library_t * lib = INT2PTR(infix_library_t *, tmp);
const char * name = SvPV_nolen(ST(1));
void * symbol = infix_library_get_symbol(lib, name);
if (symbol) {
Affix_Pin * pin;
Newxz(pin, 1, Affix_Pin);
pin->pointer = symbol;
pin->managed = false;
pin->owner_sv = ST(0);
SvREFCNT_inc(pin->owner_sv);
pin->type_arena = infix_arena_create(256);
infix_type * void_ptr_type = nullptr;
if (infix_type_create_pointer_to(pin->type_arena, &void_ptr_type, infix_type_create_void()) != INFIX_SUCCESS) {
safefree(pin);
infix_arena_destroy(pin->type_arena);
croak("Internal error: Failed to create pointer type for pin");
}
pin->type = void_ptr_type;
SV * obj_data = newSV(0);
sv_setiv(obj_data, PTR2IV(pin));
SV * rv = newRV_inc(obj_data);
// Bless into Affix::Pointer BEFORE attaching magic to avoid triggering 'set' during blessing
(void)sv_bless(rv, gv_stashpv("Affix::Pointer", GV_ADD));
MAGIC * mg = sv_magicext(obj_data, nullptr, PERL_MAGIC_ext, &Affix_pin_vtbl, nullptr, 0);
mg->mg_ptr = (char *)pin;
ST(0) = sv_2mortal(rv);
XSRETURN(1);
}
XSRETURN_UNDEF;
}
XS_INTERNAL(Affix_pin) {
dXSARGS;
dMY_CXT;
if (items != 4)
croak_xs_usage(cv, "var, lib, symbol, type");
SV * target_sv = ST(0);
const char * lib_path_or_name = SvPV_nolen(ST(1));
const char * symbol_name = SvPV_nolen(ST(2));
const char * signature = SvPV_nolen(ST(3));
infix_library_t * lib = infix_library_open(lib_path_or_name);
if (lib == nullptr) {
warn("Failed to load library from path '%s' for pinning: %s", lib_path_or_name, infix_get_last_error().message);
XSRETURN_UNDEF;
}
void * ptr = infix_library_get_symbol(lib, symbol_name);
infix_library_close(lib);
if (ptr == nullptr) {
warn("Failed to locate symbol '%s' in library '%s'", symbol_name, lib_path_or_name);
XSRETURN_UNDEF;
}
infix_type * type = nullptr;
infix_arena_t * arena = nullptr;
const char * sig_to_parse = signature;
char * clean_sig = nullptr;
if (strstr(signature, "+")) {
clean_sig = savepv(signature);
const char * p = signature;
char * d = clean_sig;
while (*p) {
// Strip '+' only if it precedes a signature character: * [ { ! < ( @
if (*p == '+' &&
(p[1] == '*' || p[1] == '[' || p[1] == '{' || p[1] == '!' || p[1] == '<' || p[1] == '(' || p[1] == '@'))
p++;
else
*d++ = *p++;
}
*d = '\0';
sig_to_parse = clean_sig;
}
if (infix_type_from_signature(&type, &arena, sig_to_parse, MY_CXT.registry) != INFIX_SUCCESS) {
SV * err_sv = _format_parse_error(aTHX_ "for pin", sig_to_parse, infix_get_last_error());
warn_sv(err_sv);
if (arena)
infix_arena_destroy(arena);
if (clean_sig)
safefree(clean_sig);
XSRETURN_UNDEF;
}
_pin_sv(aTHX_ target_sv, type, ptr, false, nullptr, 0, 0);
infix_arena_destroy(arena);
lib/Affix.c view on Meta::CPAN
printf(" %02x", pc[i]);
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
buff[i % perLine] = '.';
else
buff[i % perLine] = pc[i];
buff[(i % perLine) + 1] = '\0';
}
while ((i % perLine) != 0) {
printf(" ");
i++;
}
printf(" | %s\n", buff);
safefree(buff);
fflush(stdout);
}
void _DD(pTHX_ SV * scalar, const char * file, int line) {
Perl_load_module(aTHX_ PERL_LOADMOD_NOIMPORT, newSVpvs("Data::Printer"), nullptr, nullptr, nullptr);
if (!get_cvs("Data::Printer::p", GV_NOADD_NOINIT | GV_NO_SVGMAGIC))
return;
fflush(stdout);
dSP;
int count;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 1);
PUSHs(scalar);
PUTBACK;
count = call_pv("Data::Printer::p", G_SCALAR);
SPAGAIN;
if (count != 1) {
warn("Big trouble\n");
return;
}
STRLEN len;
const char * s = SvPVx(POPs, len);
printf("%s at %s line %d\n", s, file, line);
fflush(stdout);
PUTBACK;
FREETMPS;
LEAVE;
}
XS_INTERNAL(Affix_sv_dump) {
dXSARGS;
if (items != 1)
croak_xs_usage(cv, "sv");
sv_dump(ST(0));
XSRETURN_EMPTY;
}
SV * _new_pointer_obj(pTHX_ Affix_Pin * pin) {
SV * data_sv = newSV(0);
sv_setiv(data_sv, PTR2IV(pin));
SvUPGRADE(data_sv, SVt_PVMG);
SV * rv = newRV_noinc(data_sv);
// Bless into Affix::Pointer
(void)sv_bless(rv, gv_stashpv("Affix::Pointer", GV_ADD));
MAGIC * mg = sv_magicext(data_sv, nullptr, PERL_MAGIC_ext, &Affix_pin_vtbl, nullptr, 0);
mg->mg_ptr = (char *)pin;
return rv;
}
XS_INTERNAL(Affix_malloc) {
dXSARGS;
dMY_CXT;
if (items < 1)
croak_xs_usage(cv, "size");
UV size = SvUV(ST(0));
infix_type * type = nullptr;
infix_arena_t * parse_arena = nullptr;
const char * sig = "*void";
if (infix_type_from_signature(&type, &parse_arena, sig, MY_CXT.registry) != INFIX_SUCCESS) {
SV * err_sv = _format_parse_error(aTHX_ "for malloc", sig, infix_get_last_error());
warn_sv(err_sv);
if (parse_arena)
infix_arena_destroy(parse_arena);
XSRETURN_UNDEF;
}
if (size == 0) {
infix_arena_destroy(parse_arena);
warn("Cannot malloc a zero-sized type");
XSRETURN_UNDEF;
}
void * ptr = safemalloc(size);
Affix_Pin * pin;
Newxz(pin, 1, Affix_Pin);
pin->size = size;
pin->pointer = ptr;
pin->managed = true;
pin->type_arena = infix_arena_create(1024);
// We unwrap the pointer type logic here similar to cast.
// If the user passed "Int", we want the pin to be typed as "Int" (so $$pin reads an int).
// _unwrap_pin_type handles the logic of "don't unwrap *void or *char".
pin->type = _copy_type_graph_to_arena(pin->type_arena, _unwrap_pin_type(type));
infix_arena_destroy(parse_arena);
ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ pin));
XSRETURN(1);
}
XS_INTERNAL(Affix_calloc) {
dXSARGS;
dMY_CXT;
if (items != 2)
croak_xs_usage(cv, "count, type_signature");
UV count = SvUV(ST(0));
const char * signature = nullptr;
SV * type_sv = ST(1);
lib/Affix.c view on Meta::CPAN
signature = SvPV_nolen(type_sv);
bool live_hint = (signature[0] == '+');
if (live_hint)
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;
}
/* Value (Copy) vs Pin (Reference) */
bool return_as_value = false;
bool is_string_type = false;
if (new_type->category == INFIX_TYPE_PRIMITIVE || new_type->category == INFIX_TYPE_ENUM ||
new_type->category == INFIX_TYPE_STRUCT || new_type->category == INFIX_TYPE_UNION) {
return_as_value = true;
}
else if (new_type->category == INFIX_TYPE_POINTER) {
const infix_type * pointee = new_type->meta.pointer_info.pointee_type;
/* Check if casting to String (*char) or WString (*wchar_t) */
if (pointee->category == INFIX_TYPE_PRIMITIVE) {
if (pointee->meta.primitive_id == INFIX_PRIMITIVE_SINT8 ||
pointee->meta.primitive_id == INFIX_PRIMITIVE_UINT8 ||
/* Char check */
infix_type_get_size(pointee) == 1) {
return_as_value = true;
is_string_type = true;
}
#if defined(INFIX_OS_WINDOWS)
else if (infix_type_get_size(pointee) == sizeof(wchar_t)) {
return_as_value = true;
is_string_type = true;
}
#endif
}
}
if (return_as_value) {
/* Read memory -> Perl Scalar */
SV * ret_val = sv_newmortal();
const infix_type * resolved = _resolve_type(aTHX_ new_type);
if (is_string_type) {
/*
* String pull handlers expect a pointer-to-pointer (char**).
* 'ptr_val' IS the char*. So we pass '&ptr_val'.
* The handler reads *(&ptr_val) -> ptr_val, then reads the string.
*/
ptr2sv(aTHX_ nullptr, &ptr_val, ret_val, new_type);
}
else if (live_hint && (resolved->category == INFIX_TYPE_STRUCT || resolved->category == INFIX_TYPE_UNION)) {
// Live struct return from cast: bypass ptr2sv and create blessed HV
HV * hv = newHV();
SV * rv = newRV_noinc(MUTABLE_SV(hv));
sv_bless(rv, gv_stashpv("Affix::Live", GV_ADD));
_populate_hv_from_c_struct(
aTHX_ nullptr, hv, resolved, ptr_val, true, pin ? (pin->owner_sv ? pin->owner_sv : arg) : nullptr);
ret_val = sv_2mortal(rv);
}
else if (resolved->category == INFIX_TYPE_UNION) {
// Unions are always live!
pull_union(aTHX_ nullptr, ret_val, resolved, ptr_val);
}
else {
/*
* Primitives expect a pointer to the value.
* 'ptr_val' IS the address of the value. We pass 'ptr_val'.
* The handler reads *(int*)ptr_val.
*/
ptr2sv(aTHX_ nullptr, ptr_val, ret_val, new_type);
}
infix_arena_destroy(parse_arena);
ST(0) = ret_val;
}
else {
/* Return Alias Pin */
Affix_Pin * new_pin;
Newxz(new_pin, 1, Affix_Pin);
new_pin->pointer = ptr_val;
new_pin->managed = false;
new_pin->type_arena = parse_arena;
if (pin) {
new_pin->owner_sv = pin->owner_sv ? pin->owner_sv : arg;
SvREFCNT_inc(new_pin->owner_sv);
}
if (new_type->category == INFIX_TYPE_POINTER)
new_pin->type = _unwrap_pin_type(new_type);
else
new_pin->type = new_type;
// Create the object (SV wrapped in RV)
SV * rv = _new_pointer_obj(aTHX_ new_pin);
// Return the RV
ST(0) = sv_2mortal(rv);
}
XSRETURN(1);
}
XS_INTERNAL(Affix_own) {
dXSARGS;
if (items < 1)
croak_xs_usage(cv, "pin, [should_own]");
Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
if (!pin) {
warn("Argument is not a pinned pointer");
XSRETURN_UNDEF;
}
if (items > 1) {
// Don't dirty the memory if value hasn't changed
lib/Affix.c view on Meta::CPAN
warn("Failed to create char* type for strdup");
XSRETURN_UNDEF;
}
ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ pin));
XSRETURN(1);
}
XS_INTERNAL(Affix_strnlen) {
dXSARGS;
if (items != 2)
croak_xs_usage(cv, "ptr, maxlen");
const char * ptr = (const char *)_resolve_readable_ptr(aTHX_ ST(0));
size_t maxlen = (size_t)SvUV(ST(1));
if (!ptr)
XSRETURN_IV(0);
// strnlen is not standard C89, so we implement it manually to be safe
size_t len = 0;
while (len < maxlen && ptr[len] != '\0')
len++;
ST(0) = sv_2mortal(newSVuv(len));
XSRETURN(1);
}
XS_INTERNAL(Affix_is_null) {
dXSARGS;
if (items != 1)
croak_xs_usage(cv, "ptr");
const void * ptr = _resolve_readable_ptr(aTHX_ ST(0));
ST(0) = ptr == nullptr ? &PL_sv_yes : &PL_sv_no;
XSRETURN(1);
}
static const infix_type * _resolve_type(pTHX_ const infix_type * type) {
if (type->category == INFIX_TYPE_NAMED_REFERENCE) {
dMY_CXT;
const infix_type * resolved = infix_registry_lookup_type(MY_CXT.registry, type->meta.named_reference.name);
return resolved ? resolved : type;
}
return type;
}
void _populate_hv_from_c_struct(
pTHX_ Affix * affix, HV * hv, const infix_type * type, void * p, bool live, SV * owner_sv) {
hv_clear(hv);
const infix_type * resolved_type = _resolve_type(aTHX_ type);
for (size_t i = 0; i < resolved_type->meta.aggregate_info.num_members; ++i) {
const infix_struct_member * member = &resolved_type->meta.aggregate_info.members[i];
if (member->name) {
void * member_ptr = (char *)p + member->offset;
SV * member_sv = nullptr;
if (live) {
// Live mode: Create a Pin or Live view for this member
const infix_type * resolved = _resolve_type(aTHX_ member->type);
if (resolved->category == INFIX_TYPE_STRUCT || resolved->category == INFIX_TYPE_UNION) {
HV * sub_hv = newHV();
SV * sub_rv = newRV_noinc(MUTABLE_SV(sub_hv));
sv_bless(sub_rv, gv_stashpv("Affix::Live", GV_ADD));
_populate_hv_from_c_struct(aTHX_ affix, sub_hv, resolved, member_ptr, true, owner_sv);
member_sv = sub_rv;
}
else if (resolved->category == INFIX_TYPE_ARRAY) {
Affix_Pin * sub_pin;
Newxz(sub_pin, 1, Affix_Pin);
sub_pin->pointer = member_ptr;
sub_pin->managed = false;
if (owner_sv) {
sub_pin->owner_sv = owner_sv;
SvREFCNT_inc(sub_pin->owner_sv);
}
sub_pin->type_arena = infix_arena_create(256);
sub_pin->type = _copy_type_graph_to_arena(sub_pin->type_arena, member->type);
member_sv = _new_pointer_obj(aTHX_ sub_pin);
}
else {
member_sv = newSV(0);
_pin_sv(aTHX_ member_sv,
member->type,
member_ptr,
false,
owner_sv,
member->bit_offset,
member->bit_width);
}
}
else if (member->is_bitfield) {
// Bitfield pull: mask and shift
member_sv = newSV(0);
size_t sz = infix_type_get_size(member->type);
uint64_t raw = 0;
memcpy(&raw, member_ptr, sz);
uint64_t val = (raw >> member->bit_offset) & (((uint64_t)1 << member->bit_width) - 1);
sv_setuv(member_sv, val);
}
else {
member_sv = newSV(0);
ptr2sv(aTHX_ affix, member_ptr, member_sv, member->type);
}
if (member_sv)
hv_store(hv, member->name, strlen(member->name), member_sv, 0);
}
}
}
// Cribbed from Perl::Destruct::Level so leak testing works without yet another prereq
XS_INTERNAL(Affix_set_destruct_level) {
dVAR;
dXSARGS;
// TODO: report this with a warn(...)
if (items != 1)
croak_xs_usage(cv, "level");
PL_perl_destruct_level = SvIV(ST(0));
XSRETURN_EMPTY;
}
lib/Affix.c view on Meta::CPAN
Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
if (pin && pin->type) {
if (pin->type->category == INFIX_TYPE_ARRAY) {
ST(0) = sv_2mortal(newSVuv(pin->type->meta.array_info.num_elements));
XSRETURN(1);
}
else if (pin->type->category == INFIX_TYPE_VOID ||
(pin->type->category == INFIX_TYPE_POINTER &&
pin->type->meta.pointer_info.pointee_type->category == INFIX_TYPE_VOID)) {
// For void pointers, count is the byte size if known
if (pin->size > 0) {
ST(0) = sv_2mortal(newSVuv(pin->size));
XSRETURN(1);
}
}
}
XSRETURN_UNDEF;
}
XS_INTERNAL(Affix_pin_size) {
dXSARGS;
if (items != 1)
croak_xs_usage(cv, "pin");
Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
if (pin) {
ST(0) = sv_2mortal(newSVuv(pin->size));
XSRETURN(1);
}
XSRETURN_UNDEF;
}
XS_INTERNAL(Affix_pin_get_at) {
dXSARGS;
if (items != 2)
croak_xs_usage(cv, "pin, index");
Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
IV index = SvIV(ST(1));
if (!pin || !pin->type)
croak("Not a valid pinned pointer");
const infix_type * type = pin->type;
const infix_type * elem_type = type;
if (type->category == INFIX_TYPE_ARRAY)
elem_type = type->meta.array_info.element_type;
else if (type->category == INFIX_TYPE_POINTER && type->meta.pointer_info.pointee_type->category == INFIX_TYPE_VOID)
elem_type = type->meta.pointer_info.pointee_type;
else
croak("Cannot index into non-aggregate type");
size_t elem_size = infix_type_get_size(elem_type);
if (elem_size == 0 && elem_type->category == INFIX_TYPE_VOID) {
elem_size = 1; // Byte-indexed for void*
elem_type = infix_type_create_primitive(INFIX_PRIMITIVE_UINT8);
}
if (elem_size == 0)
croak("Cannot index into zero-sized type");
void * target = (char *)pin->pointer + (index * elem_size);
if (elem_type->category == INFIX_TYPE_STRUCT || elem_type->category == INFIX_TYPE_UNION) {
HV * hv = newHV();
SV * rv = newRV_noinc(MUTABLE_SV(hv));
sv_bless(rv, gv_stashpv("Affix::Live", GV_ADD));
// We might need to pass the owner here too, but let's start with pins
_populate_hv_from_c_struct(aTHX_ nullptr, hv, elem_type, target, true, pin->owner_sv ? pin->owner_sv : ST(0));
ST(0) = sv_2mortal(rv);
}
else if (elem_type->category == INFIX_TYPE_ARRAY) {
// Return a new Affix::Pointer for this sub-array (Live view)
Affix_Pin * new_pin;
Newxz(new_pin, 1, Affix_Pin);
new_pin->pointer = target;
new_pin->managed = false;
new_pin->owner_sv = pin->owner_sv ? pin->owner_sv : ST(0);
SvREFCNT_inc(new_pin->owner_sv);
// We need to keep the type info alive. For now, copy it.
new_pin->type_arena = infix_arena_create(256);
new_pin->type = _copy_type_graph_to_arena(new_pin->type_arena, elem_type);
ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ new_pin));
}
else {
SV * res = sv_newmortal();
ptr2sv(aTHX_ nullptr, target, res, elem_type);
ST(0) = res;
}
XSRETURN(1);
}
XS_INTERNAL(Affix_pin_set_at) {
dXSARGS;
if (items != 3)
croak_xs_usage(cv, "pin, index, value");
Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
IV index = SvIV(ST(1));
SV * val_sv = ST(2);
if (!pin || !pin->type)
croak("Not a valid pinned pointer");
const infix_type * type = pin->type;
const infix_type * elem_type = type;
if (type->category == INFIX_TYPE_ARRAY)
elem_type = type->meta.array_info.element_type;
else if (type->category == INFIX_TYPE_POINTER && type->meta.pointer_info.pointee_type->category == INFIX_TYPE_VOID)
elem_type = type->meta.pointer_info.pointee_type;
else
croak("Cannot index into non-aggregate type");
size_t elem_size = infix_type_get_size(elem_type);
if (elem_size == 0 && elem_type->category == INFIX_TYPE_VOID) {
elem_size = 1;
elem_type = infix_type_create_primitive(INFIX_PRIMITIVE_UINT8);
}
if (elem_size == 0)
croak("Cannot index into zero-sized type");
void * target = (char *)pin->pointer + (index * elem_size);
sv2ptr(aTHX_ nullptr, val_sv, target, elem_type);
XSRETURN_EMPTY;
}
// Helper to register core internal types
static void _register_core_types(infix_registry_t * registry) {
( run in 0.888 second using v1.01-cache-2.11-cpan-39bf76dae61 )