Affix

 view release on metacpan or  search on metacpan

lib/Affix/marshal.c  view on Meta::CPAN

        return 0;
    const infix_type * type = resolve_type(aTHX_ im->type);
    SvGMAGICAL_off(sv);
    uint64_t val = 0;
    size_t sz = type->size;
    if (sz == 1)
        memcpy(&val, im->ptr, 1);
    else if (sz == 2)
        memcpy(&val, im->ptr, 2);
    else if (sz == 4)
        memcpy(&val, im->ptr, 4);
    else if (sz == 8)
        memcpy(&val, im->ptr, 8);
    /* Calculate bitmask and apply overflow protection */
    uint64_t wmask = (im->bit_width == 64) ? ~0ULL : ((1ULL << im->bit_width) - 1);
    uint64_t mask = wmask << im->bit_offset;
    uint64_t new_bits = (SvUV(sv) & wmask) << im->bit_offset;
    val = (val & ~mask) | new_bits;
    if (sz == 1)
        memcpy(im->ptr, &val, 1);
    else if (sz == 2)
        memcpy(im->ptr, &val, 2);
    else if (sz == 4)
        memcpy(im->ptr, &val, 4);
    else if (sz == 8)
        memcpy(im->ptr, &val, 8);
    SvGMAGICAL_on(sv);
    return 0;
}

MGVTBL vtbl_bitfield = {get_bitfield, set_bitfield, nullptr, nullptr, free_v2_pin};

/**
 * @brief Universal closure trampoline for FFI Perl callbacks attached to Structs.
 * @details This function is invoked from native C space when a callback fires. It converts
 * the raw C arguments back into Perl variables using VTable magic, executes
 * the Perl subroutine, and marshals the result back into the expected C return type.
 */
void perl_universal_closure(infix_context_t * ctx, void * ret, void ** args) {
    dTHX;
    dSP;
    CV * perl_sub = (CV *)infix_reverse_get_user_data(ctx);
    size_t n = infix_reverse_get_num_args(ctx);

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);

    /* Push C arguments onto the Perl Stack using Affix 2.0 pull handlers */
    for (size_t i = 0; i < n; i++) {
        const infix_type * type = infix_reverse_get_arg_type(ctx, i);
        Affix_Pull puller = get_pull_handler(aTHX_ type);

        if (!puller)
            croak("Unsupported struct callback argument type");

        SV * arg_sv = newSV(0);
        puller(aTHX_ nullptr, arg_sv, type, args[i], false);
        mXPUSHs(arg_sv);
    }
    PUTBACK;

    const infix_type * ret_type = infix_reverse_get_return_type(ctx);
    U32 call_flags = G_KEEPERR | ((ret_type->category == INFIX_TYPE_VOID) ? G_VOID : G_SCALAR);

    size_t count = call_sv((SV *)perl_sub, call_flags);
    SPAGAIN;

    /* Retrieve Perl return value and pass it back to C using Affix 2.0 push handlers */
    if (SvTRUE(ERRSV)) {
        Perl_warn(aTHX_ "Perl struct callback died: %" SVf, ERRSV);
        sv_setsv(ERRSV, &PL_sv_undef);
        if (ret && !(call_flags & G_VOID))
            memset(ret, 0, infix_type_get_size(ret_type));
    }
    else if (call_flags & G_SCALAR) {
        SV * return_sv = (count == 1) ? POPs : &PL_sv_undef;
        sv2ptr(aTHX_ nullptr, return_sv, ret, ret_type);
    }

    PUTBACK;
    FREETMPS;
    LEAVE;
}

void pull_pointer_as_callable(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;
    if (type->category == INFIX_TYPE_POINTER)
        pointee = resolve_type(aTHX_ type->meta.pointer_info.pointee_type);

    SV * wrapper = wrap_callable_pointer(aTHX_ c_ptr, pointee);
    sv_setsv(sv, wrapper);
    SvREFCNT_dec(wrapper);
}

/**
 * @brief Dynamically wraps a C function pointer into a callable Perl Subroutine.
 */
SV * wrap_callable_pointer(pTHX_ void * addr, const infix_type * type) {
    if (!addr)
        return newSV(0);

    char sig[512];
    /* Serialize the AST back into a string signature */
    if (infix_type_print(sig, sizeof(sig), type, INFIX_DIALECT_SIGNATURE) != INFIX_SUCCESS) {
        warn("Affix: failed to serialize callback type signature, wrapping as raw pointer");
        return newSVuv(PTR2UV(addr));
    }

    /* Strip the callback indicator '!' so Affix::wrap treats it as a standard function */
    char * sig_ptr = sig;
    if (sig_ptr[0] == '!')
        sig_ptr++;

    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    EXTEND(SP, 2);
    /* Push arguments for Affix::wrap($address, $signature_string) */
    PUSHs(sv_2mortal(newSVuv(PTR2UV(addr))));
    PUSHs(sv_2mortal(newSVpv(sig_ptr, 0)));
    PUTBACK;

    int count = call_pv("Affix::wrap", G_SCALAR);
    SPAGAIN;

    SV * ret = newSV(0);
    if (count == 1) {
        SV * wrapped = POPs;
        if (SvOK(wrapped))
            sv_setsv(ret, wrapped);
        else
            sv_setuv(ret, PTR2UV(addr));
    }
    else {
        sv_setuv(ret, PTR2UV(addr));
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return ret;
}

int get_ptr(pTHX_ SV * sv, MAGIC * mg) {
    Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;
    SvSMAGICAL_off(sv);

    /* If it's absolute (malloc/cast/pin), im->ptr is the memory address of the data.
       If it's relative (struct member), im->ptr is the address of a pointer variable. */
    void * addr = im->absolute ? im->ptr : (im->ptr ? *(void **)im->ptr : nullptr);

    if (!addr) {
        sv_setsv(sv, &PL_sv_undef);
    }
    else {
        const infix_type * res = resolve_type(aTHX_ im->type);

        /* Detect terminal pointers (String, StringList, Callback, etc.) */
        Affix_Pull puller = get_pull_handler(aTHX_ res);
        if (puller && puller != pull_pointer_as_pin) {
            puller(aTHX_ nullptr, sv, res, &addr, im->readonly);
        }
        else {
            /* If the type is a pointer, step down one level. */
            const infix_type * pointee =
                (res->category == INFIX_TYPE_POINTER) ? res->meta.pointer_info.pointee_type : res;

            /* WString (wchar_t*): convert the wide string to a Perl UTF-8 string.
               Only uint16/uint32 pointers whose width matches wchar_t are treated
               as WString, mirroring the marshaling in get_opcode_for_type. */
            if (pointee->category == INFIX_TYPE_PRIMITIVE && pointee->size == sizeof(wchar_t) &&
                (pointee->meta.primitive_id == INFIX_PRIMITIVE_UINT16 ||
                 pointee->meta.primitive_id == INFIX_PRIMITIVE_UINT32)) {
                size_t el_sz = pointee->size;
                wchar_t * ws = (wchar_t *)addr;
                size_t act = 0;
                while (el_sz == 2 ? ((uint16_t *)ws)[act] : ((uint32_t *)ws)[act])
                    act++;
                U8 * buf = (U8 *)safemalloc(act * UTF8_MAXBYTES + 1);
                U8 * d = buf;
                for (size_t i = 0; i < act; i++) {
                    UV cp = (el_sz == 2) ? ((uint16_t *)ws)[i] : ((uint32_t *)ws)[i];
                    if (el_sz == 2 && cp >= 0xD800 && cp <= 0xDBFF && i + 1 < act) {
                        uint16_t low = ((uint16_t *)ws)[i + 1];
                        if (low >= 0xDC00 && low <= 0xDFFF) {
                            cp = 0x10000 + (((cp - 0xD800) << 10) | (low - 0xDC00));
                            i++;
                        }
                    }
                    d = uvchr_to_utf8(d, cp);
                }
                *d = '\0';
                sv_setpvn(sv, (char *)buf, d - buf);
                SvUTF8_on(sv);
                safefree(buf);
            }
            else {



( run in 1.508 second using v1.01-cache-2.11-cpan-804bf51f3ce )