#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <nanomsg/nn.h>
#include <nanomsg/pair.h>
#ifndef XS_INTERNAL
# define XS_INTERNAL(name) static XSPROTO(name)
#endif
#ifndef mg_findext
# define mg_findext(sv, type, vtbl) S_mg_findext(aTHX_ sv, type, vtbl)
static MAGIC *
S_mg_findext(pTHX_ SV *sv, int type, const MGVTBL *vtbl)
{
MAGIC *mg;
if (sv) {
for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
if (mg->mg_type == type && mg->mg_virtual == vtbl)
return mg;
}
}
return NULL;
}
#endif
#define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#define PERL_DECIMAL_VERSION \
PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#define PERL_VERSION_GE(r,v,s) \
(PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
#if PERL_VERSION_GE(5,14,0)
# define SIZETf "%zd"
# define SIZETfARG(s) s
#else
# define SIZETf "%lu"
# define SIZETfARG(s) (unsigned long)s
#endif
#ifndef dVAR
# define dVAR dNOOP
#endif
#ifndef croak_xs_usage
# define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b)
static void S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
#endif
SV *errno_sv;
HV *message_stash, *message_freed_stash;
#define PERL_NN_SET_ERRNO STMT_START { \
sv_setpv(errno_sv, nn_strerror(errno)); \
SvIV_set(errno_sv, errno); \
SvIOK_on(errno_sv); \
} STMT_END
typedef int perl_nn_int;
typedef int perl_nn_int_bool;
typedef void * perl_nn_messagebuf;
struct perl_nn_message {
void *buf;
size_t len;
};
static int
perl_nn_message_mg_dup (pTHX_ MAGIC *mg, CLONE_PARAMS *param)
{
struct perl_nn_message *dst;
struct perl_nn_message *src = (struct perl_nn_message *)mg->mg_ptr;
PERL_UNUSED_ARG(param);
Newx(dst, 1, struct perl_nn_message);
dst->len = src->len;
dst->buf = nn_allocmsg(src->len, 0); /* FIXME: alloc type */
memcpy(dst->buf, src->buf, src->len);
mg->mg_ptr = (char *)dst;
return 0;
}
static int
perl_nn_message_mg_free (pTHX_ SV *sv, MAGIC *mg)
{
struct perl_nn_message *msg = (struct perl_nn_message *)mg->mg_ptr;
PERL_UNUSED_ARG(sv);
if (msg->buf)
nn_freemsg(msg->buf);
return 0;
}
static MGVTBL perl_nn_message_vtbl = {
NULL, /* get */
NULL, /* set */
NULL, /* len */
NULL, /* clear */
perl_nn_message_mg_free, /* free */
NULL, /* copy */
perl_nn_message_mg_dup, /* dup */
NULL /* local */
};
static struct perl_nn_message *
perl_nn_message_mg_find (pTHX_ SV *sv)
{
MAGIC *mg = mg_findext(sv, PERL_MAGIC_ext, &perl_nn_message_vtbl);
return (struct perl_nn_message *)mg->mg_ptr;
}
AV *symbol_names;
XS_INTERNAL(XS_NanoMsg__Raw_nn_constant);
XS_INTERNAL(XS_NanoMsg__Raw_nn_constant)
{
dVAR;
dXSARGS;
IV ix = XSANY.any_iv;
dXSTARG;
if (items != 0)
croak_xs_usage(cv, "");
XSprePUSH;
PUSHi((IV)ix);
XSRETURN(1);
}
static struct perl_nn_message *
perl_nn_upgrade_to_message (pTHX_ SV *sv)
{
MAGIC *mg;
struct perl_nn_message *msg;
SV *obj = newSV(0);
/* There's no sane way to prepare an arbitrary SV to be a reference, so we'll
* have to go out of our way a bit. Also, we're forced to free any PV, if
* present, as PV and RV are stored in the same place within an SV. */
if (SvROK(sv))
SvREFCNT_dec(SvRV(sv));
if (SvTYPE(sv) >= SVt_PV && SvPVX_const(sv) && SvLEN(sv)) {
SvPV_free(sv);
SvPV_set(sv, NULL);
SvLEN_set(sv, 0);
}
SvUPGRADE(sv, SVt_RV);
SvPOK_off(sv);
SvRV_set(sv, obj);
SvROK_on(sv);
sv_upgrade(obj, SVt_PVMG);
SvPOK_on(obj);
SvCUR_set(obj, 0);
SvLEN_set(obj, 0);
sv_bless(sv, message_stash);
SvREADONLY_on(obj);
Newxz(msg, 1, struct perl_nn_message);
mg = sv_magicext(obj, NULL, PERL_MAGIC_ext, &perl_nn_message_vtbl, (char *)msg, 0);
mg->mg_flags |= MGf_DUP;
return msg;
}
static struct perl_nn_message *
perl_nn_invalidate_message (pTHX_ SV *sv)
{
MAGIC *mg, *prevmg, *moremg = NULL;
struct perl_nn_message *msg = NULL;
SV *obj = SvRV(sv);
SvREADONLY_off(obj);
SvPOK_off(obj);
SvPVX(obj) = NULL;
sv_bless(sv, message_freed_stash);
for (prevmg = NULL, mg = SvMAGIC(obj); mg; prevmg = mg, mg = moremg) {
moremg = mg->mg_moremagic;
if (mg->mg_type == PERL_MAGIC_ext &&
mg->mg_virtual == &perl_nn_message_vtbl) {
if (prevmg)
prevmg->mg_moremagic = moremg;
else
SvMAGIC_set(obj, moremg);
mg->mg_moremagic = NULL;
msg = (struct perl_nn_message *)mg->mg_ptr;
Safefree(mg);
mg = prevmg;
}
}
assert(msg);
return msg;
}
static bool
perl_nn_is_message (pTHX_ SV *sv)
{
return sv_isobject(sv) && SvSTASH(SvRV(sv)) == message_stash;
}
MODULE=NanoMsg::Raw PACKAGE=NanoMsg::Raw
PROTOTYPES: DISABLE
perl_nn_int
nn_socket (domain, protocol)
int domain
int protocol
perl_nn_int_bool
nn_close (s)
int s
perl_nn_int_bool
nn_setsockopt (s, level, option, optval)
int s
int level
int option
SV *optval
PREINIT:
const void *c_optval;
size_t c_optvallen;
int a_int; /* needed at this scope so we can pass a pointer to it in the
generated CODE section */
INIT:
if (SvPOKp(optval)) {
c_optval = SvPV_const(optval, c_optvallen);
}
else {
a_int = (int)SvIV(optval);
c_optval = &a_int;
c_optvallen = sizeof(int);
}
C_ARGS:
s, level, option, c_optval, c_optvallen
SV *
nn_getsockopt (s, level, option)
int s
int level
int option
PREINIT:
size_t optvallen = 256; /* maxlen of the return value without trailing \0 */
int ret;
INIT:
RETVAL = newSV(256 + 1);
(void)SvPOK_only(RETVAL);
CODE:
ret = nn_getsockopt(s, level, option, SvPVX(RETVAL), &optvallen);
POSTCALL:
if (ret < 0) {
PERL_NN_SET_ERRNO;
XSRETURN_UNDEF;
}
SvCUR_set(RETVAL, optvallen);
*SvEND(RETVAL) = '\0';
OUTPUT:
RETVAL
perl_nn_int
nn_bind (s, addr)
int s
const char *addr
perl_nn_int
nn_connect (s, addr)
int s
const char *addr
perl_nn_int_bool
nn_shutdown (s, how)
int s
int how
perl_nn_int
nn_send (s, buf, flags = 0)
int s
SV *buf
int flags
PREINIT:
void *c_buf;
size_t len;
INIT:
if (perl_nn_is_message(aTHX_ buf)) {
c_buf = &perl_nn_message_mg_find(aTHX_ SvRV(buf))->buf;
len = NN_MSG;
}
else {
c_buf = SvPV(buf, len);
}
C_ARGS:
s, c_buf, len, flags
POSTCALL:
if (len == NN_MSG)
perl_nn_invalidate_message(aTHX_ buf);
int
nn_recv (s, buf, len = NN_MSG, flags = 0)
int s
SV *buf
size_t len
int flags
PREINIT:
void *c_buf;
struct perl_nn_message *msg;
INIT:
if (len == NN_MSG) {
msg = perl_nn_upgrade_to_message(aTHX_ buf);
c_buf = &msg->buf;
}
else {
if (!SvOK(buf))
sv_setpvs(buf, "");
SvPV_force_nolen(buf);
c_buf = SvGROW(buf, len);
}
C_ARGS:
s, c_buf, len, flags
POSTCALL:
if (RETVAL < 0) {
PERL_NN_SET_ERRNO;
XSRETURN_UNDEF;
}
if (len == NN_MSG) {
SV *obj = SvRV(buf);
msg->len = RETVAL;
SvPVX(obj) = msg->buf;
SvCUR_set(obj, RETVAL);
SvPOK_on(obj);
}
else {
SvCUR_set(buf, (int)len < RETVAL ? (int)len : RETVAL);
(void)SvPOK_only(buf);
}
# TODO: cmsg
perl_nn_int
nn_sendmsg (s, flags, ...)
int s
int flags
PREINIT:
struct nn_msghdr hdr;
struct nn_iovec *iov;
int iovlen, i;
INIT:
iovlen = items - 2;
Newx(iov, iovlen, struct nn_iovec);
for (i = 0; i < iovlen; i++) {
SV *sv = ST(i + 2);
if (perl_nn_is_message(aTHX_ sv)) {
struct perl_nn_message *msg = perl_nn_message_mg_find(aTHX_ SvRV(sv));
iov[i].iov_base = &msg->buf;
iov[i].iov_len = NN_MSG;
}
else {
iov[i].iov_base = SvPV(sv, iov[i].iov_len);
}
}
memset(&hdr, 0, sizeof(hdr));
hdr.msg_iov = iov;
hdr.msg_iovlen = iovlen;
C_ARGS:
s, &hdr, flags
POSTCALL:
for (i = 0; i < iovlen; i++)
if (iov[i].iov_len == NN_MSG)
perl_nn_invalidate_message(aTHX_ ST(i + 2));
CLEANUP:
Safefree(iov);
int
nn_recvmsg (s, flags, ...)
int s
int flags
PREINIT:
struct nn_msghdr hdr;
struct nn_iovec *iov;
int iovlen, i;
size_t nbytes;
struct perl_nn_message *msg;
INIT:
iovlen = (items - 2) / 2;
Newx(iov, iovlen, struct nn_iovec);
for (i = 0; i < iovlen; i++) {
SV *svbuf = ST(i*2 + 2);
UV len = SvUV(ST(i*2 + 3));
iov[i].iov_len = len;
if (len == NN_MSG) {
msg = perl_nn_upgrade_to_message(aTHX_ svbuf);
iov[i].iov_base = &msg->buf;
}
else {
if (!SvOK(svbuf))
sv_setpvs(svbuf, "");
SvPV_force_nolen(svbuf);
SvGROW(svbuf, len);
iov[i].iov_base = SvPVX(svbuf);
}
}
memset (&hdr, 0, sizeof (hdr));
hdr.msg_iov = iov;
hdr.msg_iovlen = iovlen;
C_ARGS:
s, &hdr, flags
POSTCALL:
if (RETVAL < 0) {
PERL_NN_SET_ERRNO;
XSRETURN_UNDEF;
}
nbytes = RETVAL;
if (iovlen == 1 && iov[0].iov_len == NN_MSG) {
SV *obj = SvRV(ST(2));
msg->len = RETVAL;
SvPVX(obj) = msg->buf;
SvCUR_set(obj, RETVAL);
SvPOK_on(obj);
}
else {
for (i = 0; i < iovlen; i++) {
size_t max = iov[i].iov_len < nbytes ? iov[i].iov_len : nbytes;
SvCUR_set(ST(i*2 + 2), max);
if (nbytes > 0)
nbytes -= max;
}
}
CLEANUP:
Safefree(iov);
perl_nn_messagebuf
nn_allocmsg (size, type)
size_t size
int type
const char *
nn_strerror (errnum)
int errnum
SV *
nn_errno ()
CODE:
RETVAL = SvREFCNT_inc(errno_sv);
OUTPUT:
RETVAL
perl_nn_int_bool
nn_device (s1, s2)
int s1
int s2
INIT:
if (ST(0) == &PL_sv_undef)
s1 = -1;
if (ST(1) == &PL_sv_undef)
s2 = -1;
void
nn_term ()
void
_symbols ()
PREINIT:
int i;
PPCODE:
for (i = 0; i <= av_len(symbol_names); i++)
mXPUSHs(SvREFCNT_inc(*av_fetch(symbol_names, i, 0)));
BOOT:
symbol_names = newAV();
errno_sv = newSV(0);
sv_upgrade(errno_sv, SVt_PVIV);
message_stash = gv_stashpvs("NanoMsg::Raw::Message", 0);
message_freed_stash = gv_stashpvs("NanoMsg::Raw::Message::Freed", GV_ADD);
{
CV *cv;
const char *sym;
int val, i = 0;
char name[4096] = "NanoMsg::Raw::";
size_t prefixlen = sizeof("NanoMsg::Raw::") - 1;
while ((sym = nn_symbol(i++, &val)) != NULL) {
size_t symlen = strlen(sym);
if (strncmp(sym, "EFAULT", sizeof("EFAULT")-1) == 0)
continue;
av_push(symbol_names, newSVpv(sym, symlen));
memcpy(name + prefixlen, sym, symlen+1);
cv = newXS(name, XS_NanoMsg__Raw_nn_constant, file);
XSANY.any_iv = val;
}
memcpy(name + prefixlen, "NN_MSG", sizeof("NN_MSG"));
cv = newXS(name, XS_NanoMsg__Raw_nn_constant, file);
XSANY.any_iv = NN_MSG;
}
MODULE=NanoMsg::Raw PACKAGE=NanoMsg::Raw::Message
void
copy (sv, src)
SV *sv
SV *src
PREINIT:
const void *buf;
STRLEN len;
SV *obj;
struct perl_nn_message *msg;
INIT:
obj = SvRV(sv);
buf = SvPV(src, len);
msg = perl_nn_message_mg_find(aTHX_ obj);
if (len > msg->len)
croak("Trying to copy "SIZETf" bytes into a message buffer of size "SIZETf,
SIZETfARG(len), SIZETfARG(msg->len));
CODE:
memcpy(msg->buf, buf, len);
SvPVX(obj) = msg->buf;
SvCUR_set(obj, len);
SvPOK_on(obj);