/* vim:set ts=4 sw=4 et syntax=xs.doxygen: */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "ps_parser.h"
#include "stringstore.h"
#include "convert.h"
typedef struct self {
int flags; ///< flags controlling deserialization
SV *parent; ///< a PHP::Serialization object
} *self;
static char _error_msg[256] = "Unknown error";
static void _register_error(const char *msg)
{
strncpy(_error_msg, msg, sizeof _error_msg);
}
static void _croak(const char *msg)
{
SV *errsv = get_sv("@", TRUE);
sv_setsv(errsv, newSVpvf("%s\n", msg));
croak(Nullch);
}
// some code adapted from Heap::Simple::XS
#define C_SELF(object, context) c_self(aTHX_ object, context)
static self c_self(pTHX_ SV *object, const char *context)
{
if (!SvROK(object)) {
if (SvOK(object)) croak("%s is not a reference", context);
croak("%s is undefined", context);
}
SV *sv = SvRV(object);
if (!SvOBJECT(sv)) croak("%s is not an object reference", context);
HV *stash = SvSTASH(sv);
HV *class_stash = gv_stashpv("PHP::Serialization::XS", FALSE);
/// @todo check for isa
IV address = SvIV(sv);
return INT2PTR(self, address);
}
static void option(pTHX_ self me, SV *tag, SV *value)
{
STRLEN len, len2;
char *key = SvPV(tag, len);
char *val = SvPV(value, len2);
if (!strcmp(key, "prefer_hash")) {
if (SvIV(value)) {
me->flags |= PS_XS_PREFER_HASH;
me->flags &= ~PS_XS_PREFER_ARRAY;
me->flags &= ~PS_XS_PREFER_UNDEF;
}
} else if (!strcmp(key, "prefer_undef")) {
if (SvIV(value)) {
me->flags |= PS_XS_PREFER_UNDEF;
me->flags &= ~PS_XS_PREFER_HASH;
me->flags &= ~PS_XS_PREFER_ARRAY;
}
} else if (!strcmp(key, "prefer_array")) {
if (SvIV(value)) {
me->flags |= PS_XS_PREFER_ARRAY;
me->flags &= ~PS_XS_PREFER_HASH;
me->flags &= ~PS_XS_PREFER_UNDEF;
}
} else {
warn("Unknown option %s => %s", key, val);
}
}
MODULE = PHP::Serialization::XS PACKAGE = PHP::Serialization::XS
PROTOTYPES: ENABLE
SV *
_get_parent(self me)
CODE:
RETVAL = me->parent;
OUTPUT:
RETVAL
SV *
new(char *class, ...)
PREINIT:
self me;
CODE:
New(__LINE__, me, 1, struct self);
if (items % 2 == 0) croak("Odd number of elements in options");
me->flags = 0;
RETVAL = sv_newmortal();
sv_setref_pv(RETVAL, class, (void*) me);
for (int i = 1; i < items; i += 2) option(aTHX_ me, ST(i), ST(i + 1));
/// @todo replace this eval with a more XS-y way of calling the
/// super-class's new
/// @todo permit passing parameters to this call
me->parent = eval_pv("PHP::Serialization->new", true);
SvREFCNT_inc(me->parent);
SvREFCNT_inc(RETVAL);
OUTPUT:
RETVAL
SV *
_c_decode(self me, SV *input, ....)
CODE:
struct ps_parser_state *ps_state;
ps_parser_error_handler = _register_error;
if (ps_init(&ps_state))
_croak("ERROR: Failed to init ps_parser");
const char *str = SvPV_nolen_const(input);
ps_read_string_init(ps_state, (void*)str);
struct ps_node *node = ps_parse(ps_state);
if (node == PS_PARSE_FAILURE)
_croak(_error_msg);
const char *claxx = NULL;
if (items > 2 && SvOK(ST(2)))
claxx = (char *)SvPV_nolen(ST(2));
RETVAL = _convert_recurse(node, me->flags, claxx);
ps_free(node);
ps_fini(&ps_state);
OUTPUT:
RETVAL