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 )