Eshu
view release on metacpan or search on metacpan
t/0242-realworld-xs.t view on Meta::CPAN
MODULE = Math PACKAGE = Math
int
add(a, b)
int a
int b
CODE:
RETVAL = a + b;
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: XSUB returning int with OUTPUT');
}
# 4. XSUB returning SV*
{
my $code = <<'END';
MODULE = Str PACKAGE = Str
SV *
repeat(s, n)
const char *s
int n
CODE:
RETVAL = newSV(0);
for (int i = 0; i < n; i++) {
sv_catpv(RETVAL, s);
}
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: XSUB returning SV* with CODE/OUTPUT');
}
# 5. XSUB with INIT section
{
my $code = <<'END';
MODULE = Buf PACKAGE = Buf
SV *
read_chunk(fh, len)
FILE *fh
size_t len
INIT:
if (len == 0)
XSRETURN_UNDEF;
CODE:
char *buf = malloc(len + 1);
if (!buf)
XSRETURN_UNDEF;
size_t n = fread(buf, 1, len, fh);
buf[n] = '\0';
RETVAL = newSVpvn(buf, n);
free(buf);
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: XSUB with INIT guard');
}
# 6. PPCODE XSUB returning list
{
my $code = <<'END';
MODULE = ListOps PACKAGE = ListOps
void
range(from, to)
int from
int to
PPCODE:
for (int i = from; i <= to; i++) {
XPUSHs(sv_2mortal(newSViv(i)));
}
END
is(xs($code), $code, 'XS: PPCODE returning list');
}
# 7. XSUB with C helper function
{
my $code = <<'END';
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
static unsigned long
djb2(const char *s) {
unsigned long h = 5381;
int c;
while ((c = (unsigned char)*s++) != 0) {
h = ((h << 5) + h) + c;
}
return h;
}
MODULE = Hash PACKAGE = Hash
unsigned long
hash_string(s)
const char *s
CODE:
RETVAL = djb2(s);
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: XSUB with C helper');
}
# 8. BOOT section
{
my $code = <<'END';
MODULE = MyExt PACKAGE = MyExt
BOOT:
av_push(get_av("MyExt::ISA", GV_ADD), newSVpvs("Exporter"));
SV *ver = get_sv("MyExt::VERSION", GV_ADD);
sv_setpvs(ver, "1.00");
END
is(xs($code), $code, 'XS: BOOT section');
}
# 9. XSUB with nested C
{
my $code = <<'END';
MODULE = Crypto PACKAGE = Crypto
SV *
xor_bytes(data, key)
SV *data
SV *key
CODE:
STRLEN dlen, klen;
const char *d = SvPVbyte(data, dlen);
const char *k = SvPVbyte(key, klen);
if (klen == 0)
XSRETURN_UNDEF;
t/0242-realworld-xs.t view on Meta::CPAN
length(x, y)
double x
double y
CODE:
RETVAL = sqrt(x * x + y * y);
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: multiple XSUBs');
}
# 11. XSUB accessing Perl hash
{
my $code = <<'END';
MODULE = Obj PACKAGE = Obj
void
set_field(self, key, val)
SV *self
const char *key
SV *val
CODE:
HV *hv = (HV *)SvRV(self);
hv_store(hv, key, strlen(key), SvREFCNT_inc(val), 0);
END
is(xs($code), $code, 'XS: XSUB setting hash field');
}
# 12. XSUB reading Perl array
{
my $code = <<'END';
MODULE = Arr PACKAGE = Arr
IV
sum_array(aref)
SV *aref
CODE:
AV *av = (AV *)SvRV(aref);
IV total = 0;
for (SSize_t i = 0; i <= av_len(av); i++) {
SV **svp = av_fetch(av, i, 0);
if (svp && *svp) {
total += SvIV(*svp);
}
}
RETVAL = total;
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: XSUB summing array ref');
}
# 13. XSUB with SV mortal
{
my $code = <<'END';
MODULE = Fmt PACKAGE = Fmt
void
print_pairs(href)
SV *href
PPCODE:
HV *hv = (HV *)SvRV(href);
HE *he;
hv_iterinit(hv);
while ((he = hv_iternext(hv)) != NULL) {
SV *key = hv_iterkeysv(he);
SV *val = hv_iterval(hv, he);
XPUSHs(sv_2mortal(newSVpvf("%s=%s",
SvPVutf8_nolen(key), SvPVutf8_nolen(val))));
}
END
is(xs($code), $code, 'XS: PPCODE iterating hash ref');
}
# 14. XSUB with typemap
{
my $code = <<'END';
MODULE = File PACKAGE = File
FILE *
fopen_wrap(path, mode)
const char *path
const char *mode
CODE:
RETVAL = fopen(path, mode);
if (!RETVAL)
XSRETURN_UNDEF;
OUTPUT:
RETVAL
void
fclose_wrap(fh)
FILE *fh
CODE:
fclose(fh);
END
is(xs($code), $code, 'XS: FILE* typemap usage');
}
# 15. XSUB with C struct
{
my $code = <<'END';
typedef struct {
double x;
double y;
} Point;
MODULE = Geom PACKAGE = Geom
SV *
point_new(x, y)
double x
double y
CODE:
Point *p = malloc(sizeof(Point));
p->x = x;
p->y = y;
RETVAL = newSViv((IV)p);
OUTPUT:
RETVAL
double
point_distance(p1_sv, p2_sv)
SV *p1_sv
SV *p2_sv
CODE:
Point *p1 = (Point *)SvIV(p1_sv);
Point *p2 = (Point *)SvIV(p2_sv);
double dx = p1->x - p2->x;
double dy = p1->y - p2->y;
RETVAL = sqrt(dx*dx + dy*dy);
OUTPUT:
t/0242-realworld-xs.t view on Meta::CPAN
RETVAL
END
is(xs($code), $code, 'XS: platform ifdef inside XSUB');
}
# 21. XS with inline C
{
my $code = <<'END';
#define FAST_ABS(x) ((x) < 0 ? -(x) : (x))
MODULE = Fast PACKAGE = Fast
IV
fast_abs(n)
IV n
CODE:
RETVAL = FAST_ABS(n);
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: inline macro used in XSUB');
}
# 22. XSUB modifying caller's SV
{
my $code = <<'END';
MODULE = Ref PACKAGE = Ref
void
double_in_place(sv)
SV *sv
CODE:
sv_setiv(sv, SvIV(sv) * 2);
END
is(xs($code), $code, 'XS: modify SV in place');
}
# 23. XSUB accepting and returning AV*
{
my $code = <<'END';
MODULE = Arr2 PACKAGE = Arr2
SV *
array_reverse(aref)
SV *aref
CODE:
AV *in = (AV *)SvRV(aref);
AV *out = newAV();
SSize_t n = av_len(in);
for (SSize_t i = n; i >= 0; i--) {
SV **svp = av_fetch(in, i, 0);
av_push(out, svp ? SvREFCNT_inc(*svp) : newSV(0));
}
RETVAL = newRV_noinc((SV *)out);
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: reverse array ref');
}
# 24. XSUB with multiple return values via PPCODE
{
my $code = <<'END';
MODULE = Math2 PACKAGE = Math2
void
divmod(a, b)
IV a
IV b
PPCODE:
if (b == 0)
Perl_croak(aTHX_ "division by zero");
EXTEND(SP, 2);
PUSHs(sv_2mortal(newSViv(a / b)));
PUSHs(sv_2mortal(newSViv(a % b)));
END
is(xs($code), $code, 'XS: divmod via PPCODE');
}
# 25. XS constant sub
{
my $code = <<'END';
MODULE = Const PACKAGE = Const
IV
PI_TIMES_1000()
CODE:
RETVAL = 3141;
OUTPUT:
RETVAL
IV
MAX_UINT16()
CODE:
RETVAL = 65535;
OUTPUT:
RETVAL
END
is(xs($code), $code, 'XS: constant XSUBs');
}
# ââ normalization tests ââââââââââââââââââââââââââââââââââââââââââââ
# 26
{
my $in = <<'END';
MODULE = T PACKAGE = T
int
square(n)
int n
CODE:
RETVAL = n * n;
OUTPUT:
RETVAL
END
my $exp = <<'END';
MODULE = T PACKAGE = T
int
square(n)
int n
CODE:
RETVAL = n * n;
OUTPUT:
RETVAL
END
is(xs($in), $exp, 'XS: unindented CODE/OUTPUT normalised');
}
# 27
{
my $in = <<'END';
MODULE = T PACKAGE = T
void
hello()
t/0242-realworld-xs.t view on Meta::CPAN
{
my $in = <<'END';
MODULE = T PACKAGE = T
IV
clamp(v, lo, hi)
IV v
IV lo
IV hi
CODE:
if (v < lo) { RETVAL = lo; }
else if (v > hi) { RETVAL = hi; }
else { RETVAL = v; }
OUTPUT:
RETVAL
END
my $exp = <<'END';
MODULE = T PACKAGE = T
IV
clamp(v, lo, hi)
IV v
IV lo
IV hi
CODE:
if (v < lo) { RETVAL = lo; }
else if (v > hi) { RETVAL = hi; }
else { RETVAL = v; }
OUTPUT:
RETVAL
END
is(xs($in), $exp, 'XS: unindented clamp normalised');
}
# 29
{
my $in = <<'END';
MODULE = T PACKAGE = T
BOOT:
av_push(get_av("T::EXPORT_OK", GV_ADD), newSVpvs("square"));
END
my $exp = <<'END';
MODULE = T PACKAGE = T
BOOT:
av_push(get_av("T::EXPORT_OK", GV_ADD), newSVpvs("square"));
END
is(xs($in), $exp, 'XS: unindented BOOT normalised');
}
# 30
{
my $in = <<'END';
MODULE = T PACKAGE = T
void
each(aref, cb)
SV *aref
SV *cb
PPCODE:
AV *av = (AV *)SvRV(aref);
for (SSize_t i = 0; i <= av_len(av); i++) {
SV **svp = av_fetch(av, i, 0);
dSP; ENTER; SAVETMPS; PUSHMARK(SP);
XPUSHs(svp ? *svp : &PL_sv_undef);
PUTBACK; call_sv(cb, G_VOID|G_DISCARD);
FREETMPS; LEAVE;
}
END
my $exp = <<'END';
MODULE = T PACKAGE = T
void
each(aref, cb)
SV *aref
SV *cb
PPCODE:
AV *av = (AV *)SvRV(aref);
for (SSize_t i = 0; i <= av_len(av); i++) {
SV **svp = av_fetch(av, i, 0);
dSP; ENTER; SAVETMPS; PUSHMARK(SP);
XPUSHs(svp ? *svp : &PL_sv_undef);
PUTBACK; call_sv(cb, G_VOID|G_DISCARD);
FREETMPS; LEAVE;
}
END
is(xs($in), $exp, 'XS: unindented PPCODE with loop normalised');
}
# ââ idempotency tests ââââââââââââââââââââââââââââââââââââââââââââââ
for my $snippet (
"MODULE = T PACKAGE = T\n\nint\nadd(a,b)\nint a\nint b\nCODE:\nRETVAL=a+b;\nOUTPUT:\nRETVAL\n",
"MODULE = T PACKAGE = T\n\nvoid\nnoop()\nCODE:\n/* nothing */\n",
"#include \"EXTERN.h\"\n#include \"perl.h\"\n#include \"XSUB.h\"\nMODULE=T PACKAGE=T\nBOOT:\nSV*v=get_sv(\"T::VERSION\",GV_ADD);sv_setpvs(v,\"1.0\");\n",
"MODULE=T PACKAGE=T\nSV*\nhex_sv(n)\nUV n\nCODE:\nchar b[32];snprintf(b,sizeof(b),\"0x%llx\",(ull)n);RETVAL=newSVpv(b,0);\nOUTPUT:\nRETVAL\n",
"MODULE=T PACKAGE=T\nvoid\nfree_ptr(p)\nSV*p\nCODE:\nfree((void*)SvIV(p));\n",
"MODULE = T PACKAGE = T\n\nIV\ncount(aref)\n\tSV *aref\nCODE:\n\tRETVAL = av_len((AV*)SvRV(aref)) + 1;\nOUTPUT:\n\tRETVAL\n",
"MODULE = T PACKAGE = T\n\nSV *\nslurp(path)\n\tconst char *path\nCODE:\n\tFILE *f=fopen(path,\"r\");\n\tif(!f)XSRETURN_UNDEF;\n\tfseek(f,0,SEEK_END);\n\tlong n=ftell(f);\n\trewind(f);\n\tchar *b=malloc(n+1);\n\tfread(b,1,n,f);\n\tb[n]='\\0';\n\tfcl...
"MODULE=T PACKAGE=T\nBOOT:\nHV*stash=gv_stashpvs(\"T\",GV_ADD);\nnewCONSTSUB(stash,\"OK\",newSViv(1));\nnewCONSTSUB(stash,\"ERR\",newSViv(-1));\n",
"MODULE = T PACKAGE = T\n\nvoid\nlog_call(name, args)\n\tconst char *name\n\tSV *args\nCODE:\n\tAV *av=(AV*)SvRV(args);\n\tPerlIO_printf(PerlIO_stderr(),\"%s(%ld args)\\n\",name,(long)(av_len(av)+1));\n",
"MODULE = T PACKAGE = T\n\nIV\nbsearch_iv(aref, target)\n\tSV *aref\n\tIV target\nCODE:\n\tAV *av=(AV*)SvRV(aref);\n\tSSize_t lo=0,hi=av_len(av),mid;\n\tRETVAL=-1;\n\twhile(lo<=hi){\n\t\tmid=lo+(hi-lo)/2;\n\t\tIV v=SvIV(*av_fetch(av,mid,0));\n\t\tif...
) {
my $once = xs($snippet);
is(xs($once), $once, 'XS: snippet idempotent');
}
done_testing;
( run in 1.787 second using v1.01-cache-2.11-cpan-aadc1410aed )