Affix

 view release on metacpan or  search on metacpan

lib/Affix.c  view on Meta::CPAN

    return writeback_primitive;
}

// Dispatcher implementation details
#if defined(INFIX_COMPILER_GCC) || defined(INFIX_COMPILER_CLANG)
#define USE_THREADED_CODE 1

// Label is an address
#define OP_LABEL(op) [op] = &&CASE_##op

// Jump to next instruction
#define DISPATCH() goto * dispatch_table[(++step)->opcode]

// Jump to first instruction
#define DISPATCH_START() goto * dispatch_table[step->opcode]

// Sentinel does nothing, execution falls through
#define DISPATCH_END() (void)0

// Define the table
#define DEFINE_DISPATCH_TABLE()                                                                \
    static void * dispatch_table[] =                                                           \
        {OP_LABEL(OP_PUSH_BOOL),       OP_LABEL(OP_PUSH_SINT8),     OP_LABEL(OP_PUSH_UINT8),   \
         OP_LABEL(OP_PUSH_SINT16),     OP_LABEL(OP_PUSH_UINT16),    OP_LABEL(OP_PUSH_SINT32),  \
         OP_LABEL(OP_PUSH_UINT32),     OP_LABEL(OP_PUSH_SINT64),    OP_LABEL(OP_PUSH_UINT64),  \
         OP_LABEL(OP_PUSH_FLOAT),      OP_LABEL(OP_PUSH_FLOAT16),   OP_LABEL(OP_PUSH_DOUBLE),  \
         OP_LABEL(OP_PUSH_LONGDOUBLE), OP_LABEL(OP_PUSH_SINT128),   OP_LABEL(OP_PUSH_UINT128), \
         OP_LABEL(OP_PUSH_PTR_CHAR),   OP_LABEL(OP_PUSH_PTR_WCHAR), OP_LABEL(OP_PUSH_POINTER), \
         OP_LABEL(OP_PUSH_SV),         OP_LABEL(OP_PUSH_STRUCT),    OP_LABEL(OP_PUSH_UNION),   \
         OP_LABEL(OP_PUSH_ARRAY),      OP_LABEL(OP_PUSH_CALLBACK),  OP_LABEL(OP_PUSH_ENUM),    \
         OP_LABEL(OP_PUSH_COMPLEX),    OP_LABEL(OP_PUSH_VECTOR),    OP_LABEL(OP_DONE)};
#else
#define USE_THREADED_CODE 0

// Label is a case statement
#define OP_LABEL(op) case op:

// Break to loop again
#define DISPATCH() \
    step++;        \
    break

// Start loop and switch
#define DISPATCH_START() \
    while (1) {          \
        switch (step->opcode) {

// Close switch and break loop
#define DISPATCH_END() \
    }                  \
    break;             \
    }

// No table needed
#define DEFINE_DISPATCH_TABLE()
#endif

// Forward declaration for the lazy rebuilder
static void rebuild_affix_data(pTHX_ Affix * affix);

// We use a macro to generate two variants (Stack vs Arena) to ensure logic sync.
#define GENERATE_TRIGGER_XSUB(NAME, USE_STACK_ALLOC)                                                         \
    void NAME(pTHX_ CV * cv) {                                                                               \
        if (UNLIKELY(PL_dirty))                                                                              \
            return;                                                                                          \
        dSP;                                                                                                 \
        dAXMARK;                                                                                             \
        dXSTARG;                                                                                             \
        Affix * affix = (Affix *)CvXSUBANY(cv).any_ptr;                                                      \
                                                                                                             \
        /* LAZY REBUILD: If we are in a new thread and data hasn't been built yet */                         \
        if (UNLIKELY(!affix->infix))                                                                         \
            rebuild_affix_data(aTHX_ affix);                                                                 \
                                                                                                             \
        if (UNLIKELY((SP - MARK) != affix->num_args))                                                        \
            croak("Wrong number of arguments. Expected %d, got %d", (int)affix->num_args, (int)(SP - MARK)); \
                                                                                                             \
        register Affix_Plan_Step * step = affix->plan;                                                       \
                                                                                                             \
        /* ALLOCATION STRATEGY */                                                                            \
        size_t arena_mark = affix->args_arena->current_offset;                                               \
        void * args_buffer;                                                                                  \
        if (USE_STACK_ALLOC && affix->total_args_size <= 2048) {                                             \
            /* Fast path: Stack allocation if under 2k */                                                    \
            args_buffer = alloca(affix->total_args_size);                                                    \
            memset(args_buffer, 0, affix->total_args_size);                                                  \
        }                                                                                                    \
        else {                                                                                               \
            /* Slow path: Arena allocation */                                                                \
            arena_mark = affix->args_arena->current_offset;                                                  \
            /* Alignment 64 is safe for AVX-512 vectors */                                                   \
            args_buffer = infix_arena_calloc(affix->args_arena, 1, affix->total_args_size, 64);              \
        }                                                                                                    \
                                                                                                             \
        register void ** c_args = (void **)alloca(affix->num_args * sizeof(void *));                         \
        memset(c_args, 0, affix->num_args * sizeof(void *));                                                 \
                                                                                                             \
        size_t ret_align = affix->ret_type->alignment;                                                       \
        if (ret_align < 1)                                                                                   \
            ret_align = 1;                                                                                   \
        void * ret_buffer = infix_arena_calloc(affix->ret_arena, 1, affix->ret_type->size, ret_align);       \
                                                                                                             \
        DEFINE_DISPATCH_TABLE();                                                                             \
                                                                                                             \
        DISPATCH_START();                                                                                    \
                                                                                                             \
CASE_OP_PUSH_BOOL:                                                                                           \
        {                                                                                                    \
            SV * sv = ST(step->data.index);                                                                  \
            void * ptr = (char *)args_buffer + step->data.c_arg_offset;                                      \
            *(bool *)ptr = SvTRUE(sv);                                                                       \
            c_args[step->data.index] = ptr;                                                                  \
            DISPATCH();                                                                                      \
        }                                                                                                    \
CASE_OP_PUSH_SINT8:                                                                                          \
        {                                                                                                    \
            SV * sv = ST(step->data.index);                                                                  \
            void * ptr = (char *)args_buffer + step->data.c_arg_offset;                                      \
            *(int8_t *)ptr = (int8_t)SvIV(sv);                                                               \
            c_args[step->data.index] = ptr;                                                                  \
            DISPATCH();                                                                                      \

lib/Affix.c  view on Meta::CPAN


    // Prepare JIT types (handle array decay)
    infix_type ** jit_arg_types = NULL;
    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) {
                infix_type * ptr_type = NULL;
                status = infix_type_create_pointer_to(parse_arena, &ptr_type, t->meta.array_info.element_type);
                if (status != INFIX_SUCCESS) {
                    if (parse_arena)
                        infix_arena_destroy(parse_arena);
                    croak("Affix failed to rebuild in new thread: type clone error");
                }
                jit_arg_types[i] = ptr_type;
            }
            else
                jit_arg_types[i] = t;
        }
    }

    // Create trampoline
    status =
        infix_forward_create_manual(&affix->infix, ret_type, jit_arg_types, num_args, num_fixed, affix->target_addr);

    if (jit_arg_types)
        safefree(jit_arg_types);

    if (status != INFIX_SUCCESS) {
        infix_arena_destroy(parse_arena);
        croak("Affix failed to rebuild trampoline in new thread");
    }

    affix->cif = infix_forward_get_code(affix->infix);
    affix->ret_type = infix_forward_get_return_type(affix->infix);
    affix->unwrapped_ret_type = _unwrap_pin_type(affix->ret_type);
    affix->ret_pull_handler = get_pull_handler(aTHX_ affix->ret_type);
    // affix->ret_opcode is already set from parent, but safe to assume it matches

    // Allocate arenas & SV
    affix->args_arena = infix_arena_create(4096);
    affix->ret_arena = infix_arena_create(1024);
    affix->return_sv = newSV(0);
    if (affix->num_args > 0)
        Newx(affix->c_args, affix->num_args, void *);

    affix->variadic_cache = newHV();

    // Rebuild plan
    Newxz(affix->plan, affix->plan_length + 1, Affix_Plan_Step);

    size_t out_param_count = 0;
    OutParamInfo * temp_out_info = safemalloc(sizeof(OutParamInfo) * (affix->num_args > 0 ? affix->num_args : 1));
    size_t current_offset = 0;

    for (size_t i = 0; i < affix->num_args; ++i) {
        // Deep copy types from parse_arena to persistent args_arena
        const infix_type * original_type = _copy_type_graph_to_arena(affix->args_arena, args[i].type);

        // Recalculate offsets (logic duplication from Affix_affix, but necessary)
        size_t alignment, size;
        if (original_type->category == INFIX_TYPE_ARRAY) {
            alignment = _Alignof(void *);
            size = sizeof(void *);
        }
        else {
            alignment = infix_type_get_alignment(original_type);
            size = infix_type_get_size(original_type);
        }
        if (alignment == 0)
            alignment = 1;

        current_offset = (current_offset + alignment - 1) & ~(alignment - 1);
        affix->plan[i].data.c_arg_offset = current_offset;
        current_offset += size;

        affix->plan[i].executor = get_plan_step_executor(original_type);
        affix->plan[i].opcode = get_opcode_for_type(original_type);
        affix->plan[i].data.type = original_type;
        affix->plan[i].data.index = i;

        // Re-detect out params
        if (original_type->category == INFIX_TYPE_POINTER) {
            const infix_type * pointee = original_type->meta.pointer_info.pointee_type;
            const char * pointee_name = infix_type_get_name(pointee);
            bool is_sv_pointer = pointee_name && (strEQ(pointee_name, "SV") || strEQ(pointee_name, "@SV"));

            if (!is_sv_pointer && pointee->category != INFIX_TYPE_REVERSE_TRAMPOLINE &&
                pointee->category != INFIX_TYPE_VOID) {
                temp_out_info[out_param_count].perl_stack_index = i;
                temp_out_info[out_param_count].pointee_type = pointee;
                temp_out_info[out_param_count].writer = get_out_param_writer(pointee);
                out_param_count++;
            }
        }
        else if (original_type->category == INFIX_TYPE_ARRAY) {
            temp_out_info[out_param_count].perl_stack_index = i;
            temp_out_info[out_param_count].pointee_type = original_type;
            temp_out_info[out_param_count].writer = affix_array_writeback;
            out_param_count++;
        }
    }
    affix->plan[affix->num_args].opcode = OP_DONE;

    // Setup OUT params
    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);
    }
    safefree(temp_out_info);

    // Done. parse_arena can go.
    infix_arena_destroy(parse_arena);
}


static MGVTBL Affix_cv_vtbl = {0, 0, 0, 0, Affix_cv_free, 0, Affix_cv_dup, 0};

static MGVTBL Affix_coercion_vtbl = {0};  // Marker vtable for coerced values

lib/Affix.c  view on Meta::CPAN

            "Not enough arguments for variadic function. Expected at least %zu, got %zu", affix->num_fixed_args, items);

    // Construct the complete signature string dynamically
    SV * sig_sv = sv_2mortal(newSVpv("", 0));

    // Reconstruct fixed part from the cached sig_str (which ends in '; ...' or similar)
    // We need to parse the original signature string to get the fixed part cleanly,
    // OR we can reconstruct it from the plan.
    // Simplest: The affix->sig_str contains the fixed part and the ';'.
    // We assume affix->sig_str is like "(*char; ...)->int"

    char * semi_ptr = strchr(affix->sig_str, ';');
    if (!semi_ptr)
        croak("Internal error: Variadic function missing semicolon in signature");

    // Append fixed part up to and including ';'
    sv_catpvn(sig_sv, affix->sig_str, (semi_ptr - affix->sig_str) + 1);

    // Iterate varargs to infer types and append to signature
    for (size_t i = affix->num_fixed_args; i < items; ++i) {
        SV * arg = ST(i);
        const char * coerced_sig = _get_coerced_sig(aTHX_ arg);

        if (i > affix->num_fixed_args)
            sv_catpvs(sig_sv, ",");

        if (coerced_sig)
            sv_catpv(sig_sv, coerced_sig);
        else if (is_pin(aTHX_ arg))
            // It's a pointer/struct pin. We treat it as a void pointer for the signature
            // unless we can introspect the pin's type object deeply.
            // For now, let's treat pins as '*void' (opaque pointer) in varargs unless coerced.
            sv_catpvs(sig_sv, "*void");
        else if (SvIOK(arg))
            sv_catpvs(sig_sv, "sint64");  // Default integer promotion
        else if (SvNOK(arg))
            sv_catpvs(sig_sv, "double");  // Default float promotion
        else if (SvPOK(arg))
            sv_catpvs(sig_sv, "*char");  // Default string promotion
        else                             // Fallback/Unknown
            sv_catpvs(sig_sv, "sint64");
    }

    // Append return type part (find ')' in original sig)
    char * close_paren = strrchr(affix->sig_str, ')');
    if (close_paren)
        sv_catpv(sig_sv, close_paren);
    else
        croak("Malformed signature string in affix");

    const char * full_sig = SvPV_nolen(sig_sv);

    // Check Cache
    infix_forward_t * trampoline = NULL;
    SV ** cache_entry = hv_fetch(affix->variadic_cache, full_sig, strlen(full_sig), 0);

    if (cache_entry)
        trampoline = INT2PTR(infix_forward_t *, SvIV(*cache_entry));
    else {
        // Cache Miss: Compile new trampoline
        // We use the parsing logic to get types
        infix_arena_t * temp_arena = NULL;
        infix_type * ret_type = NULL;
        infix_function_argument * args = NULL;
        size_t num_args = 0, num_fixed = 0;

        infix_status status =
            infix_signature_parse(full_sig, &temp_arena, &ret_type, &args, &num_args, &num_fixed, MY_CXT.registry);

        if (status != INFIX_SUCCESS) {
            if (temp_arena)
                infix_arena_destroy(temp_arena);
            croak("Failed to compile variadic signature: %s", full_sig);
        }

        // Convert args to type array
        infix_type ** arg_types = NULL;
        if (num_args > 0) {
            arg_types = safemalloc(sizeof(infix_type *) * num_args);
            for (size_t i = 0; i < num_args; ++i)
                arg_types[i] = args[i].type;
        }

        status = infix_forward_create_manual(&trampoline, ret_type, arg_types, num_args, num_fixed, affix->target_addr);

        if (arg_types)
            safefree(arg_types);
        infix_arena_destroy(temp_arena);

        if (status != INFIX_SUCCESS)
            croak("Failed to create variadic trampoline");

        // Store in cache
        hv_store(affix->variadic_cache, full_sig, strlen(full_sig), newSViv(PTR2IV(trampoline)), 0);
    }

    // Execute
    infix_cif_func cif = infix_forward_get_code(trampoline);
    size_t num_args = infix_forward_get_num_args(trampoline);
    const infix_type * ret_type = infix_forward_get_return_type(trampoline);

    // Allocate args buffer (pointers)
    void ** c_args = alloca(sizeof(void *) * num_args);

    // Use an arena for argument data to avoid many malloc/frees
    infix_arena_t * call_arena = infix_arena_create(2048);
    void * ret_buffer = infix_arena_alloc(call_arena, infix_type_get_size(ret_type), 8);

    // Marshal Arguments
    for (size_t i = 0; i < num_args; ++i) {
        const infix_type * arg_type = infix_forward_get_arg_type(trampoline, i);
        void * data = infix_arena_alloc(call_arena, infix_type_get_size(arg_type), infix_type_get_alignment(arg_type));
        sv2ptr(aTHX_ affix, ST(i), data, arg_type);
        c_args[i] = data;
    }

    // Call
    cif(ret_buffer, c_args);

    // Marshal Return
    SV * ret_sv = TARG;

lib/Affix.c  view on Meta::CPAN


XS_INTERNAL(Affix_sv_dump) {
    dXSARGS;
    if (items != 1)
        croak_xs_usage(cv, "sv");
    sv_dump(ST(0));
    XSRETURN_EMPTY;
}

SV * _new_pointer_obj(pTHX_ Affix_Pin * pin) {
    SV * data_sv = newSV(0);
    sv_setiv(data_sv, PTR2IV(pin));
    SvUPGRADE(data_sv, SVt_PVMG);

    SV * rv = newRV_noinc(data_sv);

    // Bless into Affix::Pointer
    (void)sv_bless(rv, gv_stashpv("Affix::Pointer", GV_ADD));

    MAGIC * mg = sv_magicext(data_sv, nullptr, PERL_MAGIC_ext, &Affix_pin_vtbl, nullptr, 0);
    mg->mg_ptr = (char *)pin;

    return rv;
}

XS_INTERNAL(Affix_malloc) {
    dXSARGS;
    dMY_CXT;

    if (items < 1)
        croak_xs_usage(cv, "size");

    UV size = SvUV(ST(0));
    infix_type * type = nullptr;
    infix_arena_t * parse_arena = nullptr;

    const char * sig = "*void";

    if (infix_type_from_signature(&type, &parse_arena, sig, MY_CXT.registry) != INFIX_SUCCESS) {
        SV * err_sv = _format_parse_error(aTHX_ "for malloc", sig, infix_get_last_error());
        warn_sv(err_sv);
        if (parse_arena)
            infix_arena_destroy(parse_arena);
        XSRETURN_UNDEF;
    }

    if (size == 0) {
        infix_arena_destroy(parse_arena);
        warn("Cannot malloc a zero-sized type");
        XSRETURN_UNDEF;
    }

    void * ptr = safemalloc(size);
    Affix_Pin * pin;
    Newxz(pin, 1, Affix_Pin);
    pin->size = size;
    pin->pointer = ptr;
    pin->managed = true;
    pin->type_arena = infix_arena_create(1024);

    // We unwrap the pointer type logic here similar to cast.
    // If the user passed "Int", we want the pin to be typed as "Int" (so $$pin reads an int).
    // _unwrap_pin_type handles the logic of "don't unwrap *void or *char".
    pin->type = _copy_type_graph_to_arena(pin->type_arena, _unwrap_pin_type(type));

    infix_arena_destroy(parse_arena);
    ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ pin));
    XSRETURN(1);
}

XS_INTERNAL(Affix_calloc) {
    dXSARGS;
    dMY_CXT;
    if (items != 2)
        croak_xs_usage(cv, "count, type_signature");
    UV count = SvUV(ST(0));
    const char * signature = nullptr;
    SV * type_sv = ST(1);
    signature = _get_string_from_type_obj(aTHX_ type_sv);
    if (!signature)
        signature = SvPV_nolen(type_sv);

    infix_type * elem_type = nullptr;
    infix_arena_t * parse_arena = nullptr;
    if (infix_type_from_signature(&elem_type, &parse_arena, signature, MY_CXT.registry) != INFIX_SUCCESS) {
        SV * err_sv = _format_parse_error(aTHX_ "for calloc", signature, infix_get_last_error());
        warn_sv(err_sv);
        if (parse_arena)
            infix_arena_destroy(parse_arena);
        XSRETURN_UNDEF;
    }
    size_t elem_size = infix_type_get_size(elem_type);
    if (elem_size == 0) {
        infix_arena_destroy(parse_arena);
        warn("Cannot calloc a zero-sized type");
        XSRETURN_UNDEF;
    }
    void * ptr = safecalloc(count, elem_size);
    Affix_Pin * pin;
    Newxz(pin, 1, Affix_Pin);
    pin->pointer = ptr;
    pin->managed = true;
    pin->type_arena = infix_arena_create(1024);
    infix_type * array_type;
    if (infix_type_create_array(pin->type_arena, &array_type, elem_type, count) != INFIX_SUCCESS) {
        safefree(pin);
        if (ptr)
            safefree(ptr);
        infix_arena_destroy(pin->type_arena);
        infix_arena_destroy(parse_arena);
        warn("Failed to create array type graph.");
        XSRETURN_UNDEF;
    }
    pin->type = array_type;
    pin->size = (count * elem_size);
    infix_arena_destroy(parse_arena);
    ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ pin));
    XSRETURN(1);
}

XS_INTERNAL(Affix_realloc) {
    dXSARGS;
    if (items != 2)

lib/Affix.c  view on Meta::CPAN

    int val = (int)SvIV(ST(1));
    size_t n = (size_t)SvUV(ST(2));
    void * res = memchr(ptr, val, n);
    if (res) {
        Affix_Pin * new_pin;
        Newxz(new_pin, 1, Affix_Pin);
        new_pin->pointer = res;
        new_pin->managed = false;

        Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
        new_pin->owner_sv = pin ? (pin->owner_sv ? pin->owner_sv : ST(0)) : ST(0);
        SvREFCNT_inc(new_pin->owner_sv);

        new_pin->type_arena = infix_arena_create(128);
        new_pin->type =
            _copy_type_graph_to_arena(new_pin->type_arena, infix_type_create_primitive(INFIX_PRIMITIVE_SINT8));
        ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ new_pin));
        XSRETURN(1);
    }
    XSRETURN_UNDEF;
}

XS_INTERNAL(Affix_ptr_add) {
    dXSARGS;
    if (items != 2)
        croak_xs_usage(cv, "ptr, offset_bytes");

    Affix_Pin * pin = _get_pin_from_sv(aTHX_ ST(0));
    void * ptr_val = nullptr;
    const infix_type * type = nullptr;

    if (pin) {
        ptr_val = pin->pointer;
        type = pin->type;
    }
    else if (SvIOK(ST(0))) {
        ptr_val = INT2PTR(void *, SvUV(ST(0)));
        type = nullptr;
    }
    else {
        warn("ptr must be a pinned pointer or address");
        XSRETURN_UNDEF;
    }

    IV offset = SvIV(ST(1));
    void * new_addr = (char *)ptr_val + offset;

    Affix_Pin * new_pin;
    Newxz(new_pin, 1, Affix_Pin);
    new_pin->pointer = new_addr;
    new_pin->managed = false;  // Aliases are never managed
    new_pin->type_arena = infix_arena_create(256);

    if (pin) {
        new_pin->owner_sv = pin->owner_sv ? pin->owner_sv : ST(0);
        SvREFCNT_inc(new_pin->owner_sv);
    }

    if (type) {
        if (type->category == INFIX_TYPE_ARRAY) {
            // Decay Array[T] -> Pointer[T] logic.
            // When adding an offset to an array, the result is a pointer to the element type,
            // not a new array. This allows dereferencing assignment ($$ptr = val) to work correctly.
            const infix_type * elem_src = type->meta.array_info.element_type;
            const infix_type * elem_copy = _copy_type_graph_to_arena(new_pin->type_arena, elem_src);

            // Cast to (infix_type*) to satisfy signature; safe because we just allocated it in our arena.
            if (infix_type_create_pointer_to(
                    new_pin->type_arena, (infix_type **)&new_pin->type, (infix_type *)elem_copy) != INFIX_SUCCESS) {
                infix_arena_destroy(new_pin->type_arena);
                safefree(new_pin);
                warn("Failed to create decayed array pointer type in ptr_add");
                XSRETURN_UNDEF;
            }
        }
        else {
            // Standard pointer arithmetic, keep the same type (e.g., int* + 4 -> int*)
            new_pin->type = _copy_type_graph_to_arena(new_pin->type_arena, type);
        }
    }
    else {
        // Fallback to *void for raw addresses
        infix_type * void_t = infix_type_create_void();
        if (infix_type_create_pointer_to(new_pin->type_arena, (infix_type **)&new_pin->type, void_t) != INFIX_SUCCESS) {
            infix_arena_destroy(new_pin->type_arena);
            safefree(new_pin);
            warn("Failed to create void* type in ptr_add");
            XSRETURN_UNDEF;
        }
    }

    ST(0) = sv_2mortal(_new_pointer_obj(aTHX_ new_pin));
    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);

lib/Affix.c  view on Meta::CPAN

        XSUB_EXPORT(get_last_error_message, "", "core");

        // Scalar pins
        XSUB_EXPORT(pin, "$$$$", "pin");
        XSUB_EXPORT(unpin, "$", "pin");

        // Introspection
        XSUB_EXPORT(sizeof, "$", "core");
        XSUB_EXPORT(alignof, "$", "core");
        XSUB_EXPORT(offsetof, "$$", "core");

        // Type registry
        (void)newXSproto_portable("Affix::_typedef", Affix_typedef, __FILE__, "$;$");
        (void)newXSproto_portable("Affix::_register_enum_values", Affix_register_enum_values, __FILE__, "$$$");
        (void)newXSproto_portable("Affix::types", Affix_defined_types, __FILE__, "");

        // Debugging
        (void)newXSproto_portable("Affix::sv_dump", Affix_sv_dump, __FILE__, "$");

        // Memory management & pointers
        XSUB_EXPORT(address, "$", "memory");
        XSUB_EXPORT(malloc, "$", "memory");
        XSUB_EXPORT(calloc, "$$", "memory");
        XSUB_EXPORT(realloc, "$$", "memory");
        XSUB_EXPORT(free, "$", "memory");
        XSUB_EXPORT(cast, "$$", "memory");
        XSUB_EXPORT(dump, "$$", "memory");
        XSUB_EXPORT(own, "$;$", "memory");

        // Raw memory operations
        XSUB_EXPORT(memcpy, "$$$", "memory");
        XSUB_EXPORT(memmove, "$$$", "memory");
        XSUB_EXPORT(memset, "$$$", "memory");
        XSUB_EXPORT(memcmp, "$$$", "memory");
        XSUB_EXPORT(memchr, "$$$", "memory");

        // Pointer utils
        XSUB_EXPORT(ptr_add, "$$", "memory");
        XSUB_EXPORT(ptr_diff, "$$", "memory");
        XSUB_EXPORT(strdup, "$", "memory");
        XSUB_EXPORT(strnlen, "$$", "memory");
        XSUB_EXPORT(is_null, "$", "memory");

        // Pin internals (for Affix::Pointer)
        (void)newXSproto_portable("Affix::_pin_type", Affix_pin_type, __FILE__, "$");
        (void)newXSproto_portable("Affix::_pin_element_type", Affix_pin_element_type, __FILE__, "$");
        (void)newXSproto_portable("Affix::_pin_count", Affix_pin_count, __FILE__, "$");
        (void)newXSproto_portable("Affix::_pin_size", Affix_pin_size, __FILE__, "$");
        (void)newXSproto_portable("Affix::_pin_get_at", Affix_pin_get_at, __FILE__, "$$");
        (void)newXSproto_portable("Affix::_pin_set_at", Affix_pin_set_at, __FILE__, "$$$");
        (void)newXSproto_portable("Affix::_attach_destructor", Affix_attach_destructor, __FILE__, "$$;$");
    }

    XSUB_EXPORT(coerce, "$$", "core");

    XSUB_EXPORT(errno, "", "core");
    (void)newXSproto_portable("Affix::set_destruct_level", Affix_set_destruct_level, __FILE__, "$");

#undef XSUB_EXPORT

    Perl_xs_boot_epilog(aTHX_ ax);
}



( run in 0.749 second using v1.01-cache-2.11-cpan-97f6503c9c8 )