Affix
view release on metacpan or search on metacpan
lib/Affix.c view on Meta::CPAN
if (ret_sv && _is_const_obj(aTHX_ ret_sv))
backend->ret_readonly = true;
else if (sig_sv && _is_const_obj(aTHX_ sig_sv))
backend->ret_readonly = true;
infix_status status =
infix_signature_parse(signature, &parse_arena, &ret_type, &args, &num_args, &num_fixed, MY_CXT.registry);
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(aTHX_ 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 = nullptr;
infix_type * ret_type = nullptr;
infix_function_argument * args = nullptr;
size_t num_args = 0, num_fixed = 0;
infix_status status =
infix_signature_parse(signature, &parse_arena, &ret_type, &args, &num_args, &num_fixed, MY_CXT.registry);
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 = nullptr;
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 = nullptr;
// 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");
}
jit_arg_types[i] = ptr_type;
}
else
jit_arg_types[i] = t;
}
}
// Object init & trampoline generation
Affix * affix;
Newxz(affix, 1, Affix);
affix->return_sv = newSV(0);
affix->variadic_cache = newHV();
bool is_variadic = (strstr(signature, ";") != nullptr);
affix->sig_str = savepv(signature);
if (rename_str)
affix->sym_name = savepv(rename_str);
affix->target_addr = symbol;
if (lib_handle_for_symbol)
affix->lib_handle = lib_handle_for_symbol;
lib/Affix.c view on Meta::CPAN
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};
size_t proto_pos = 0;
if (affix->num_args < sizeof(prototype_buf) - 1) {
memset(prototype_buf, '$', affix->num_args);
proto_pos = affix->num_args;
}
else {
memset(prototype_buf, '$', sizeof(prototype_buf) - 2);
proto_pos = sizeof(prototype_buf) - 2;
}
prototype_buf[proto_pos] = '\0';
// 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)
lib/Affix.c view on Meta::CPAN
SvREFCNT_dec(imag_sv);
}
static void pull_vector(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p, bool readonly) {
AV * av;
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) {
av = (AV *)SvRV(sv);
}
else {
av = newAV();
SV * rv = newRV_noinc(MUTABLE_SV(av));
sv_setsv(sv, rv);
SvREFCNT_dec(rv);
}
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);
Affix_Pull h = get_pull_handler(aTHX_ element_type);
if (!h)
croak("Cannot convert C vector element type to Perl SV");
for (size_t i = 0; i < num_elements; ++i) {
void * element_ptr = (char *)p + (i * element_size);
SV ** existing_sv_ptr = av_fetch(av, i, 0);
SV * element_sv = existing_sv_ptr ? *existing_sv_ptr : newSV(0);
h(aTHX_ affix, element_sv, element_type, element_ptr, readonly);
if (!existing_sv_ptr) {
if (!av_store(av, i, element_sv))
SvREFCNT_dec(element_sv);
}
}
}
static void pull_pointer_as_string(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p, bool readonly) {
void * c_ptr = *(void **)p;
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 * p, bool readonly) {
void * c_ptr = *(void **)p;
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, readonly);
}
}
#if 0
static void pull_struct_as_live(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p, bool readonly) {
void * c_ptr = *(void **)p;
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, readonly);
sv_setsv(sv, rv);
SvREFCNT_dec(rv);
}
#endif
static void pull_pointer_as_array(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p, bool readonly) {
void * c_ptr = *(void **)p;
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, readonly);
}
}
void pull_pointer_as_pin(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p, bool readonly) {
/* p is the address of the pointer variable in the C stack frame */
void * c_ptr = *(void **)p;
if (c_ptr == nullptr) {
sv_setsv(sv, &PL_sv_undef);
return;
}
const infix_type * pointee = _unwrap_pin_type(type);
const infix_type * res_pointee = resolve_type(aTHX_ pointee);
infix_type_category cat = infix_type_get_category(res_pointee);
if (cat == INFIX_TYPE_STRUCT || cat == INFIX_TYPE_UNION || cat == INFIX_TYPE_ARRAY || cat == INFIX_TYPE_VECTOR) {
/* Return HashRef or ArrayRef for complex types */
SV * rv = bind_aggregate(aTHX_ c_ptr, pointee, NULL, readonly);
sv_setsv(sv, rv);
SvREFCNT_dec(rv);
}
else {
/* FOR PRIMITIVES: Return a SCALAR REFERENCE to a magical scalar.
This allows the user to use $$p_a to read the value. */
SV * magic_scalar = newSV(0);
bind_placeholder(aTHX_ magic_scalar, c_ptr, pointee, 0, 0, true, NULL, NULL, readonly, true);
SV * rv = newRV_noinc(magic_scalar);
sv_setsv(sv, rv);
SvREFCNT_dec(rv);
}
}
static void pull_sv(pTHX_ Affix * affix, SV * sv, const infix_type * type, void * p, bool readonly) {
void * c_ptr = *(void **)p;
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 * p, bool readonly) {
PERL_UNUSED_VAR(affix);
PERL_UNUSED_VAR(type);
FILE * fp = *(FILE **)p;
if (!fp) {
sv_setsv(sv, &PL_sv_undef);
return;
}
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);
}
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) {
SV * sv = newSV(0);
/* Symbols are addresses. Use 'void' type so address() returns the symbol value itself. */
bind_placeholder(aTHX_ sv, symbol, infix_type_create_void(), 0, 0, false, ST(0), nullptr, false, true);
ST(0) = sv_2mortal(newRV_noinc(sv));
XSRETURN(1);
}
XSRETURN_UNDEF;
}
XS_INTERNAL(Affix_sizeof) {
dXSARGS;
dMY_CXT;
if (items != 1)
croak_xs_usage(cv, "type_signature");
SV * type_sv = ST(0);
if (SvIOK(type_sv) && !sv_isobject(type_sv)) {
ST(0) = sv_2mortal(newSVuv(SvUV(type_sv)));
XSRETURN(1);
}
lib/Affix.c view on Meta::CPAN
croak_xs_usage(cv, "size");
UV size = SvUV(ST(0));
if (size == 0)
XSRETURN_UNDEF;
SV * mem_obj = sv_2mortal(alloc_owned(aTHX_ size));
ST(0) = sv_2mortal(cast(aTHX_ mem_obj, "*void"));
XSRETURN(1);
}
XS_INTERNAL(Affix_calloc) {
dXSARGS;
dMY_CXT;
if (items != 2)
croak_xs_usage(cv, "count, size");
UV count = SvUV(ST(0));
UV size = SvUV(ST(1));
if (size != 0 && count > SIZE_MAX / size)
croak("Affix::calloc: integer overflow (%" UVuf " x %" UVuf ")", count, size);
SV * mem_obj = sv_2mortal(alloc_owned(aTHX_ count * size));
ST(0) = sv_2mortal(cast(aTHX_ mem_obj, "*void"));
XSRETURN(1);
}
XS_INTERNAL(Affix_realloc) {
dXSARGS;
if (items != 2)
croak_xs_usage(cv, "self, new_size");
SV * arg = ST(0);
UV new_size = SvUV(ST(1));
// 1. Extract the current pointer address robustly
void * old_ptr = get_address_v2(aTHX_ arg);
if (!old_ptr) {
warn("realloc: argument is not a valid pinned pointer or memory handle");
XSRETURN_NO;
}
// 2. Perform the reallocation
void * new_ptr = saferealloc(old_ptr, new_size);
if (!new_ptr)
XSRETURN_NO;
// 3. Update the local Pin metadata if the argument is a Pin
Affix_Pin_2_Point_Oh * pin = get_pin_v2(aTHX_ arg);
if (pin)
pin->ptr = new_ptr;
// 4. Update the Root Lifeline (the Affix::Memory internal container)
// We trace back to the SV/AV that actually holds the address for the GC.
SV * owner = _borrow_lifeline(aTHX_ arg);
if (owner) {
/* owner may be the blessed Affix::Memory RV (whose referent holds the
address) or a raw storage SV. Unwrap the RV so we never write into
the reference header itself. */
SV * storage = SvROK(owner) ? SvRV(owner) : owner;
if (SvTYPE(storage) == SVt_PVAV) {
SV ** p = av_fetch((AV *)storage, 0, 0);
if (p && *p)
sv_setuv(*p, PTR2UV(new_ptr));
}
else {
sv_setuv(storage, PTR2UV(new_ptr));
}
}
XSRETURN_YES;
}
XS_INTERNAL(Affix_own) {
dXSARGS;
if (items < 1)
croak_xs_usage(cv, "pin, [should_own]");
/* V2 memory is managed via Affix::Memory objects.
Check if the object is an Affix::Memory handle. */
if (sv_isobject(ST(0)) && sv_derived_from(ST(0), "Affix::Memory"))
XSRETURN_YES;
XSRETURN_NO;
}
XS_INTERNAL(Affix_free) {
dXSARGS;
if (items != 1)
croak_xs_usage(cv, "pointer_object");
SV * arg = ST(0);
SV * owner = _borrow_lifeline(aTHX_ arg);
if (owner && sv_isobject(owner) && sv_derived_from(owner, "Affix::Memory")) {
free_owned(aTHX_ owner);
XSRETURN_YES;
}
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);
lib/Affix.c view on Meta::CPAN
if (!ptr_val)
XSRETURN_UNDEF;
IV offset = SvIV(ST(1));
void * new_addr = (char *)ptr_val + offset;
const infix_type * type = nullptr;
bool readonly = false;
Affix_Pin_2_Point_Oh * pin = get_pin_v2(aTHX_ ST(0));
if (pin) {
type = pin->type;
readonly = pin->readonly;
}
SV * owner = _borrow_lifeline(aTHX_ ST(0));
if (type && type->category == INFIX_TYPE_ARRAY)
type = type->meta.array_info.element_type;
if (!type)
type = infix_type_create_void();
SV * sv = newSV(0);
/* Set absolute=true: new_addr is the direct target location */
bind_placeholder(aTHX_ sv, new_addr, type, 0, 0, false, owner, nullptr, readonly, true);
ST(0) = sv_2mortal(newRV_noinc(sv));
XSRETURN(1);
}
XS_INTERNAL(Affix_ptr_diff) {
dXSARGS;
if (items != 2)
croak_xs_usage(cv, "ptr1, ptr2");
// Use resolve_readable to accept pins or ints
const void * p1 = _resolve_readable_ptr(aTHX_ ST(0));
const void * p2 = _resolve_readable_ptr(aTHX_ ST(1));
if (!p1 || !p2)
XSRETURN_UNDEF;
IV diff = (const char *)p1 - (const char *)p2;
ST(0) = sv_2mortal(newSViv(diff));
XSRETURN(1);
}
XS_INTERNAL(Affix_strdup) {
dXSARGS;
if (items != 1)
croak_xs_usage(cv, "string");
STRLEN len;
const char * str = SvPV(ST(0), len);
// Allocate managed memory
char * dup = safemalloc(len + 1);
memcpy(dup, str, len);
dup[len] = '\0';
/* Wrap in an Affix::Memory to ensure it gets freed */
SV * mem_obj = newSVuv(PTR2UV(dup));
SV * mem_rv = sv_2mortal(newRV_noinc(mem_obj));
sv_bless(mem_rv, gv_stashpv("Affix::Memory", GV_ADD));
/* Cast to *void to return a persistent pin instead of a copied string */
ST(0) = sv_2mortal(cast(aTHX_ mem_rv, "*void"));
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) {
const infix_type * curr = type;
// Recursively resolve named references (typedef aliases) to find the base type.
while (curr && curr->category == INFIX_TYPE_NAMED_REFERENCE) {
dMY_CXT;
const infix_type * resolved = infix_registry_lookup_type(MY_CXT.registry, curr->meta.named_reference.name);
if (!resolved || resolved == curr)
break;
curr = resolved;
}
return curr;
}
void _populate_hv_from_c_struct(
pTHX_ Affix * affix, HV * hv, const infix_type * type, void * p, bool live, SV * owner_sv, bool readonly) {
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 ** existing_sv_ptr = hv_fetch(hv, member->name, strlen(member->name), 0);
SV * member_sv = existing_sv_ptr ? *existing_sv_ptr : nullptr;
if (member->is_bitfield) {
// Bitfield pull: mask and shift
if (!member_sv)
( run in 0.901 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )