Affix

 view release on metacpan or  search on metacpan

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

            }
            else 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)) {
                /* WString (wchar_t*): convert the Perl UTF-8 string to UTF-16/32.
                   Mirror CASE_OP_PUSH_PTR_WCHAR. Buffer lifetime matches the pin's
                   arena (struct liveness) when available, mirroring StringList. */
                STRLEN wlen;
                U8 * s = (U8 *)SvPVutf8(sv, wlen);
                U8 * e = s + wlen;
                size_t el_sz = pointee->size;
                wchar_t * wbuf = (wchar_t *)(im->arena ? infix_arena_alloc(im->arena, (wlen + 1) * el_sz, el_sz)
                                                       : safemalloc((wlen + 1) * el_sz));
                wchar_t * d = wbuf;
                while (s < e) {
                    UV uv = utf8_to_uvchr_buf(s, e, nullptr);
                    if (el_sz == 2 && uv > 0xFFFF) {
                        uv -= 0x10000;
                        *d++ = (wchar_t)((uv >> 10) + 0xD800);
                        *d++ = (wchar_t)((uv & 0x3FF) + 0xDC00);
                    }
                    else
                        *d++ = (wchar_t)uv;
                    s += UTF8SKIP(s);
                }
                *d = 0;
                new_addr = wbuf;
            }
            else {
                new_addr = INT2PTR(void *, SvUV(sv));
            }
        }
        else {
            new_addr = INT2PTR(void *, SvUV(sv));
        }
    }

    /* Priority 5: Existing Pins/Addresses */
    else {
        void * extracted = _extract_pointer_value(aTHX_ sv, mg);
        new_addr = extracted ? extracted : (SvIOK(sv) ? INT2PTR(void *, SvUV(sv)) : nullptr);
    }

    *(void **)im->ptr = new_addr;

    SvGMAGICAL_on(sv);
    return 0;
}

MGVTBL vtbl_pointer = {get_ptr, set_ptr, nullptr, nullptr, free_v2_pin};

int array_mg_fetch(pTHX_ SV * sv, MAGIC * mg) {
    /* This is triggered when someone does $array[i] */
    /* However, for MAGIC_ext on AVs, Perl doesn't call svt_get for indices. */
    /* To truly protect indices, we'd need to hook into the AV's vtable. */
    /* For now, we will focus on the bind_aggregate logic to prevent creation of OOB pins. */
    return 0;
}

/**
 * @brief Returns the length of an Infix C array so Perl's `scalar(@arr)` works.
 */
U32 array_mg_len(pTHX_ SV * sv, MAGIC * mg) {
    Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;
    if (!im->ptr)
        return 0;
    const infix_type * t = resolve_type(aTHX_ im->type);
    size_t len = (t->category == INFIX_TYPE_ARRAY) ? t->meta.array_info.num_elements : t->meta.vector_info.num_elements;
    return (U32)(len > 0 ? len - 1 : 0);
}

MGVTBL vtbl_array = {nullptr, nullptr, (U32 (*)(pTHX_ SV *, MAGIC *))array_mg_len, nullptr, free_v2_pin};

/**
 * @brief Lazy-loads child structures from memory when a Perl user tries to interact with a struct.
 */
int lazy_agg_get(pTHX_ SV * sv, MAGIC * mg) {
    Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;
    if (SvROK(sv) || SvTYPE(sv) >= SVt_PVAV)
        return 0;

    SvSMAGICAL_off(sv);
    if (!im->ptr) {
        SV * rv = _bind_aggregate_internal(aTHX_ nullptr, im->type, mg->mg_obj, im->arena, true, im->readonly);
        sv_setsv(sv, rv);
        SvREFCNT_dec(rv);
        im->arena = nullptr;
    }
    else {
        SV * rv = _bind_aggregate_internal(aTHX_ im->ptr, im->type, mg->mg_obj, im->arena, true, im->readonly);
        if (SvROK(sv))
            sv_unref(sv);
        im->arena = nullptr;
        sv_setsv(sv, rv);
        SvREFCNT_dec(rv);
    }
    SvSMAGICAL_on(sv);
    return 0;
}

/**
 * @brief True if the SV is a pin still bound to the given C slot.
 * @details During a deep write (lazy_agg_set) the members/elements we placed at
 * bind time are read back and written out again, which is a no-op for the C
 * memory. Worse, reading a *union* member's pin back can dereference a garbage
 * pointer (e.g. an inactive String/pointer member such as SDL_Event's
 * `drop.file`, whose slot overlaps the active member's float data) and crash.
 * Untouched pins are skipped instead.
 */
static bool is_same_slot_pin(pTHX_ SV * sv, void * expected_ptr, const infix_type * expected_type) {
    if (!is_pin_v2(aTHX_ sv))
        return false;
    Affix_Pin_2_Point_Oh * im = get_pin_v2(aTHX_ sv);
    return im && im->ptr == expected_ptr && im->type == expected_type;
}

/**
 * @brief Performs deep writes to structs when a user assigns a hash (`$struct = { x => 1 }`).
 */
int lazy_agg_set(pTHX_ SV * sv, MAGIC * mg) {
    Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;

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

    if (SvPOK(sv) && !SvIOK(sv)) {
        const char * type_name = infix_type_get_name(im->type);
        if (!type_name && im->type->category == INFIX_TYPE_NAMED_REFERENCE)
            type_name = im->type->meta.named_reference.name;
        if (!type_name) {
            const infix_type * resolved = resolve_type(aTHX_ im->type);
            type_name = infix_type_get_name(resolved);
        }

        if (type_name) {
            SV ** info_ptr = hv_fetch(MY_CXT.enum_registry, type_name, strlen(type_name), 0);
            if (info_ptr && SvROK(*info_ptr)) {
                HV * enum_info = (HV *)SvRV(*info_ptr);
                SV ** consts_ptr = hv_fetch(enum_info, "consts", 6, 0);
                if (consts_ptr && SvROK(*consts_ptr)) {
                    STRLEN len;
                    const char * name = SvPV(sv, len);
                    SV ** val_sv = hv_fetch((HV *)SvRV(*consts_ptr), name, len, 0);
                    if (val_sv) {
                        val = SvIV(*val_sv);
                        goto do_write;
                    }
                }
            }
        }
    }
    val = SvIV(sv);

do_write:
    {
        const infix_type * enum_type = resolve_type(aTHX_ im->type);
        const infix_type * underlying = resolve_type(aTHX_ enum_type->meta.enum_info.underlying_type);
        size_t sz = underlying->size;
        if (sz == 1)
            *(int8_t *)im->ptr = (int8_t)val;
        else if (sz == 2)
            *(int16_t *)im->ptr = (int16_t)val;
        else if (sz == 4)
            *(int32_t *)im->ptr = (int32_t)val;
        else
            *(int64_t *)im->ptr = (int64_t)val;
    }
    SvGMAGICAL_on(sv);
    return 0;
}

/* Register the VTable */
MGVTBL vtbl_enum = {get_enum, set_enum, nullptr, nullptr, free_v2_pin};

int buffer_mg_get(pTHX_ SV * sv, MAGIC * mg) {
    Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;
    const infix_type * t = resolve_type(aTHX_ im->type);
    /* For arrays, num_elements is our buffer size */
    size_t max_len = t->meta.array_info.num_elements;

    SvSMAGICAL_off(sv);
    if (!im->ptr) {
        sv_setsv(sv, &PL_sv_undef);
    }
    else {
        /* FIX: Use sv_setpvn to copy the full length, nulls and all */
        sv_setpvn(sv, (char *)im->ptr, max_len);
    }
    SvSMAGICAL_on(sv);
    return 0;
}

int buffer_mg_set(pTHX_ SV * sv, MAGIC * mg) {
    Affix_Pin_2_Point_Oh * im = (Affix_Pin_2_Point_Oh *)mg->mg_ptr;
    if (im->readonly)
        croak("Modification of a read-only C value attempted");

    const infix_type * t = resolve_type(aTHX_ im->type);
    size_t max_len = t->meta.array_info.num_elements;

    if (!im->ptr || max_len == 0)
        return 0;

    SvGMAGICAL_off(sv);
    STRLEN len;
    char * str = SvPV(sv, len);

    /* Copy only what fits, but do NOT append a null terminator */
    size_t to_copy = (len < max_len) ? len : max_len;
    memcpy(im->ptr, str, to_copy);

    SvGMAGICAL_on(sv);
    return 0;
}

MGVTBL vtbl_buffer = {buffer_mg_get, buffer_mg_set, NULL, NULL, free_v2_pin};

/**
 * @brief Binds a single C value (primitive or placeholder) to a Perl SV with magic.
 * @param sv The Perl Scalar to bind.
 * @param ptr Pointer to the raw C memory.
 * @param type The infix_type definition.
 * @param bit_offset Offset in bits (for bitfields).
 * @param bit_width Width in bits (for bitfields).
 * @param prime If true, immediately synchronize the SV with C memory.
 * @param owner The "root" SV that owns the C memory (lifeline reference tracking).
 */
void bind_placeholder(pTHX_ SV * sv,
                      void * ptr,
                      const infix_type * type,
                      uint8_t bit_offset,
                      uint8_t bit_width,
                      bool prime,
                      SV * owner,
                      infix_arena_t * arena,
                      bool readonly,
                      bool absolute) {
    const infix_type * res = resolve_type(aTHX_ type);
    infix_type_category cat = infix_type_get_category(res);

    Affix_Pin_2_Point_Oh m = {.ptr = ptr,
                              .type = type,
                              .arena = arena,
                              .bit_offset = bit_offset,
                              .bit_width = bit_width,
                              .readonly = readonly,



( run in 1.513 second using v1.01-cache-2.11-cpan-364913b4093 )