Affix
view release on metacpan or search on metacpan
lib/Affix.c view on Meta::CPAN
// SPECIAL CASE: Do NOT unwrap char* (Pointer[Int8]).
if (pointee->category == INFIX_TYPE_PRIMITIVE &&
(pointee->meta.primitive_id == INFIX_PRIMITIVE_SINT8 ||
pointee->meta.primitive_id == INFIX_PRIMITIVE_UINT8)) {
return type;
}
if (pointee->category != INFIX_TYPE_VOID)
return pointee;
}
return type;
}
// Robustly check if a type represents an SV* (or aliased version thereof)
static bool is_perl_sv_type(const infix_type * t) {
if (!t)
return false;
// Check by name
const char * name = infix_type_get_name(t);
if (name && (strEQ(name, "SV") || strEQ(name, "@SV")))
return true;
// Fallback: Structural check for the opaque SV struct defined in boot_Affix.
// This catches typedef aliases: typedef MySV => SV;
if (t->category == INFIX_TYPE_STRUCT && t->meta.aggregate_info.num_members == 1) {
const char * mname = t->meta.aggregate_info.members[0].name;
if (mname && strEQ(mname, "__sv_opaque"))
return true;
}
return false;
}
static const char * _get_string_from_type_obj(pTHX_ SV * type_sv) {
const char * str = nullptr;
if (sv_isobject(type_sv) && sv_derived_from(type_sv, "Affix::Type")) {
if (SvROK(type_sv)) {
SV * rv = SvRV(type_sv);
if (SvTYPE(rv) == SVt_PVHV) {
HV * hv = (HV *)rv;
SV ** sig_sv_ptr = hv_fetchs(hv, "signature", 0);
if (sig_sv_ptr && SvPOK(*sig_sv_ptr))
str = SvPV_nolen(*sig_sv_ptr);
else {
SV ** stringify_sv_ptr = hv_fetchs(hv, "stringify", 0);
if (stringify_sv_ptr && SvPOK(*stringify_sv_ptr))
str = SvPV_nolen(*stringify_sv_ptr);
}
}
}
}
if (!str)
str = SvPV_nolen(type_sv);
// Promote "SV" to "@SV" so the parser sees it as a named type.
// This allows the infix parser to recognize it as a named type.
if (str && strstr(str, "SV")) {
SV * modified = sv_newmortal();
sv_setpvn(modified, "", 0);
const char * p = str;
const char * start = str;
while ((p = strstr(p, "SV"))) {
// Check boundaries: Ensure we match whole word "SV"
// Start boundary: Beginning of string OR prev char is not alnum/_/@
bool start_ok = (p == start) || (!isALNUM((unsigned char)*(p - 1)) && *(p - 1) != '_' && *(p - 1) != '@');
// End boundary: End of string OR next char is not alnum/_
bool end_ok = (p[2] == '\0') || (!isALNUM((unsigned char)p[2]) && p[2] != '_');
if (start_ok && end_ok) {
// Append everything before this match
sv_catpvn(modified, start, p - start);
// Append the named type reference
sv_catpvs(modified, "@SV");
// Advance past "SV"
p += 2;
start = p;
}
else // Not a standalone "SV", skip this occurrence
p++;
}
// Append remainder
sv_catpv(modified, start);
// only return modified string if we actually changed something
if (SvCUR(modified) > strlen(str))
return SvPV_nolen(modified);
}
return str;
}
int64_t affix_perl_shim_sv_to_sint64(pTHX_ void * sv_raw) { return SvIVX((SV *)sv_raw); }
double affix_perl_shim_sv_to_double(pTHX_ void * sv_raw) { return SvNVX((SV *)sv_raw); }
const char * affix_perl_shim_sv_to_string(pTHX_ void * sv_raw) { return SvPV_nolen((SV *)sv_raw); }
void * affix_perl_shim_sv_to_pointer(pTHX_ void * sv_raw) {
SV * sv = (SV *)sv_raw;
if (!SvOK(sv) || !SvROK(sv))
return nullptr;
return INT2PTR(void *, SvIV(SvRV(sv)));
}
void * affix_perl_shim_newSViv(pTHX_ int64_t value) { return newSViv(value); }
void * affix_perl_shim_newSVnv(pTHX_ double value) { return newSVnv(value); }
void * affix_perl_shim_newSVpv(pTHX_ const char * value) { return newSVpv(value, 0); }
static int Affix_get_pin(pTHX_ SV * sv, MAGIC * mg);
static int Affix_set_pin(pTHX_ SV * sv, MAGIC * mg);
static U32 Affix_len_pin(pTHX_ SV * sv, MAGIC * mg);
static int Affix_free_pin(pTHX_ SV * sv, MAGIC * mg);
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);
static void push_union(pTHX_ Affix * affix, const infix_type * type, SV * sv, void * p);
// Execution plan for mainline Affix
static void plan_step_push_bool(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_sint8(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_uint8(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_sint16(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_uint16(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_sint32(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_uint32(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_sint64(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_uint64(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_float(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_double(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_long_double(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
#if !defined(INFIX_COMPILER_MSVC)
static void plan_step_push_sint128(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_uint128(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
#endif
static void plan_step_push_pointer(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_struct(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_union(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_array(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_enum(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_complex(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
static void plan_step_push_vector(pTHX_ Affix *, Affix_Plan_Step *, SV **, void *, void **, void *);
lib/Affix.c view on Meta::CPAN
// affix(undef, [$ptr, 'name'], ...)
if (_get_pin_from_sv(aTHX_ * sym_sv))
symbol = _get_pin_from_sv(aTHX_ * sym_sv)->pointer;
else if (SvIOK(*sym_sv))
symbol = INT2PTR(void *, SvUV(*sym_sv));
else
symbol_name_str = SvPV_nolen(*sym_sv);
}
else {
// Name a Scalar? (string or raw pointer)
// wrap(undef, $ptr, ...)
if (_get_pin_from_sv(aTHX_ name_sv))
symbol = _get_pin_from_sv(aTHX_ name_sv)->pointer;
else if (SvIOK(name_sv))
symbol = INT2PTR(void *, SvUV(name_sv));
else {
// It's a string name
symbol_name_str = SvPV_nolen(name_sv);
rename_str = symbol_name_str;
}
}
// Only load library if we don't have a direct symbol pointer yet
if (!symbol) {
if (sv_isobject(target_sv) && sv_derived_from(target_sv, "Affix::Lib")) {
IV tmp = SvIV((SV *)SvRV(target_sv));
lib_handle_for_symbol = INT2PTR(infix_library_t *, tmp);
_lib_registry_inc_ref(aTHX_ lib_handle_for_symbol);
created_implicit_handle = true;
}
else {
const char * path = SvOK(target_sv) ? SvPV_nolen(target_sv) : nullptr;
lib_handle_for_symbol = _get_lib_from_registry(aTHX_ path);
if (lib_handle_for_symbol)
created_implicit_handle = true;
}
if (lib_handle_for_symbol && symbol_name_str)
symbol = infix_library_get_symbol(lib_handle_for_symbol, symbol_name_str);
}
if (symbol == nullptr) {
if (created_implicit_handle) {
const char * lookup_path = SvOK(target_sv) ? SvPV_nolen(target_sv) : "";
SV ** entry_sv_ptr = hv_fetch(MY_CXT.lib_registry, lookup_path, strlen(lookup_path), 0);
if (entry_sv_ptr) {
LibRegistryEntry * entry = INT2PTR(LibRegistryEntry *, SvIV(*entry_sv_ptr));
entry->ref_count--;
if (entry->ref_count == 0) {
infix_library_close(entry->lib);
safefree(entry);
hv_delete_ent(MY_CXT.lib_registry, newSVpvn(lookup_path, strlen(lookup_path)), G_DISCARD, 0);
}
}
}
warn("Failed to locate symbol '%s'", symbol_name_str ? symbol_name_str : "(null)");
XSRETURN_UNDEF;
}
}
// Argument shifting (Determine where signature starts)
SV * args_sv = nullptr;
SV * ret_sv = nullptr;
SV * sig_sv = nullptr;
bool explicit_args = false; // true if using [Args] => Ret format
if (is_raw_ptr_target) {
// Mode A: wrap($ptr, [Args], Ret) -> items=3
// Mode B: wrap($ptr, "Signature") -> items=2
if (items == 3) {
args_sv = ST(1);
ret_sv = ST(2);
explicit_args = true;
}
else
sig_sv = ST(1);
}
else {
// Mode C: wrap($lib, $name, [Args], Ret) -> items=4
// Mode D: wrap($lib, $name, "Signature") -> items=3
if (items == 4) {
args_sv = ST(2);
ret_sv = ST(3);
explicit_args = true;
}
else
sig_sv = ST(2);
}
// Build infix signature string
char signature_buf[1024] = {0};
const char * signature = nullptr;
if (explicit_args) {
if (!SvROK(args_sv) || SvTYPE(SvRV(args_sv)) != SVt_PVAV)
croak("Usage: affix(..., \\@args, $ret_type) - args must be an array reference");
strcat(signature_buf, "(");
AV * args_av = (AV *)SvRV(args_sv);
SSize_t num_args = av_len(args_av) + 1;
for (SSize_t i = 0; i < num_args; ++i) {
SV ** type_sv_ptr = av_fetch(args_av, i, 0);
if (!type_sv_ptr)
continue;
const char * arg_sig = _get_string_from_type_obj(aTHX_ * type_sv_ptr);
if (!arg_sig)
croak("Invalid type object in signature");
strcat(signature_buf, arg_sig);
// Logic to prevent adding commas around ';', which denotes VarArgs start
if (i < num_args - 1) {
if (strEQ(arg_sig, ";"))
continue;
SV ** next_sv_ptr = av_fetch(args_av, i + 1, 0);
if (next_sv_ptr) {
const char * next_sig = _get_string_from_type_obj(aTHX_ * next_sv_ptr);
if (next_sig && strEQ(next_sig, ";"))
continue;
}
strcat(signature_buf, ",");
}
}
strcat(signature_buf, ") -> ");
const char * ret_sig = _get_string_from_type_obj(aTHX_ ret_sv);
if (!ret_sig)
croak("Invalid return type object");
strcat(signature_buf, ret_sig);
signature = signature_buf;
}
else {
signature = _get_string_from_type_obj(aTHX_ sig_sv);
if (!signature)
signature = SvPV_nolen(sig_sv);
}
// Direct marshalling path
if (ix == 2) {
Affix_Backend * backend;
Newxz(backend, 1, Affix_Backend);
infix_arena_t * parse_arena = nullptr;
infix_type * ret_type = nullptr;
infix_function_argument * args = nullptr;
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);
lib/Affix.c view on Meta::CPAN
PERL_UNUSED_VAR(affix);
dMY_CXT;
SV * coderef_cv = nullptr;
if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
coderef_cv = SvRV(sv);
else if (SvTYPE(sv) == SVt_PVCV)
coderef_cv = sv;
if (coderef_cv) {
char key[32];
snprintf(key, sizeof(key), "%p", (void *)coderef_cv);
SV ** entry_sv_ptr = hv_fetch(MY_CXT.callback_registry, key, strlen(key), 0);
if (entry_sv_ptr) {
Implicit_Callback_Magic * magic_data = INT2PTR(Implicit_Callback_Magic *, SvIV(*entry_sv_ptr));
*(void **)p = infix_reverse_get_code(magic_data->reverse_ctx);
}
else {
Affix_Callback_Data * cb_data;
Newxz(cb_data, 1, Affix_Callback_Data);
cb_data->coderef_rv = newRV_inc(coderef_cv);
storeTHX(cb_data->perl);
infix_type * ret_type = type->meta.func_ptr_info.return_type;
size_t num_args = type->meta.func_ptr_info.num_args;
size_t num_fixed_args = type->meta.func_ptr_info.num_fixed_args;
infix_type ** arg_types = nullptr;
if (num_args > 0) {
Newx(arg_types, num_args, infix_type *);
for (size_t i = 0; i < num_args; ++i)
arg_types[i] = type->meta.func_ptr_info.args[i].type;
}
infix_reverse_t * reverse_ctx = nullptr;
infix_status status = infix_reverse_create_closure_manual(&reverse_ctx,
ret_type,
arg_types,
num_args,
num_fixed_args,
(void *)_affix_callback_handler_entry,
(void *)cb_data);
if (arg_types)
Safefree(arg_types);
if (status != INFIX_SUCCESS) {
SvREFCNT_dec(cb_data->coderef_rv);
safefree(cb_data);
croak("Failed to create callback: %s", infix_get_last_error().message);
}
Implicit_Callback_Magic * magic_data;
Newxz(magic_data, 1, Implicit_Callback_Magic);
magic_data->reverse_ctx = reverse_ctx;
hv_store(MY_CXT.callback_registry, key, strlen(key), newSViv(PTR2IV(magic_data)), 0);
*(void **)p = infix_reverse_get_code(reverse_ctx);
}
}
else if (!SvOK(sv))
*(void **)p = nullptr;
else
croak("Argument for a callback must be a code reference or undef.");
}
static SV * _format_parse_error(pTHX_ const char * context_msg, const char * signature, infix_error_details_t err) {
STRLEN sig_len = strlen(signature);
int radius = 20;
size_t start = (err.position > radius) ? (err.position - radius) : 0;
size_t end = (err.position + radius < sig_len) ? (err.position + radius) : sig_len;
const char * start_indicator = (start > 0) ? "... " : "";
const char * end_indicator = (end < sig_len) ? " ..." : "";
int start_indicator_len = (start > 0) ? 4 : 0;
char snippet[128];
snprintf(
snippet, sizeof(snippet), "%s%.*s%s", start_indicator, (int)(end - start), signature + start, end_indicator);
char pointer[128];
int caret_pos = err.position - start + start_indicator_len;
snprintf(pointer, sizeof(pointer), "%*s^", caret_pos, "");
return sv_2mortal(newSVpvf("Failed to parse signature %s:\n\n %s\n %s\n\nError: %s (at position %zu)",
context_msg,
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));
lib/Affix.c view on Meta::CPAN
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) {
// Register SV as a named type (dummy struct ensures it keeps the name in the registry).
( run in 0.532 second using v1.01-cache-2.11-cpan-f56aa216473 )