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 )