FU
view release on metacpan or search on metacpan
c/jsonfmt.c view on Meta::CPAN
if (i >= numkeys) {
numkeys += numkeys >> 1;
keys = (HE **)SvGROW(keys_sv, numkeys * sizeof(HE*));
numkeys = SvLEN(keys_sv) / sizeof(HE*);
}
keys[i++] = he;
}
qsort(keys, i, sizeof(HE *), fujson_fmt_hvcmp);
while (i--) fujson_fmt_hvkv(aTHX_ ctx, hv, keys[i], &hestr);
FREETMPS;
} else {
while ((he = hv_iternext(hv))) fujson_fmt_hvkv(aTHX_ ctx, hv, he, &hestr);
}
ctx->pretty--;
if (hestr) fujson_fmt_indent(aTHX_ ctx);
fustr_write_ch(ctx->out, '}');
}
static void fujson_fmt_obj(pTHX_ fujson_fmt_ctx *ctx, SV *rv, SV *obj) {
dSP;
GV *method = gv_fetchmethod_autoload(SvSTASH(obj), "TO_JSON", 0);
if (!method) croak("unable to format '%s' object as JSON", HvNAME(SvSTASH(obj)));
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(rv);
PUTBACK;
call_sv((SV *)GvCV(method), G_SCALAR);
SPAGAIN;
/* JSON::XS describes this error as "surprisingly common"... I'd be
* surprised indeed if it happens at all, but I suppose it can't hurt to
* copy their check; this sounds like be a pain to debug otherwise. */
if (SvROK(TOPs) && SvRV(TOPs) == obj)
croak("%s::TO_JSON method returned same object as was passed instead of a new one", HvNAME(SvSTASH(obj)));
obj = POPs;
PUTBACK;
fujson_fmt(aTHX_ ctx, obj);
FREETMPS;
LEAVE;
}
static void fujson_fmt(pTHX_ fujson_fmt_ctx *ctx, SV *val) {
SvGETMAGIC(val);
int r = fu_2bool(aTHX_ val);
if (r != -1) { /* Must check SvISBOOL() before IOKp & POKp, because it implies both flags */
if (r) fustr_write(ctx->out, "true", 4);
else fustr_write(ctx->out, "false", 5);
} else if (SvPOKp(val)) {
fujson_fmt_str(aTHX_ ctx, SvPVX(val), SvCUR(val), SvUTF8(val));
} else if (SvNOKp(val)) { /* Must check before IOKp, because integer conversion might have been lossy */
NV nv = SvNV_nomg(val);
if (isinfnan(nv)) croak("unable to format floating point NaN or Inf as JSON");
/* XXX: Cpanel::JSON::XS appears to always append a ".0" for round numbers, other modules do not. */
/* XXX#2: This doesn't support quadmath. Makefile.PL checks for that */
fustr_reserve(ctx->out, NV_DIG+32);
Gconvert(nv, NV_DIG, 0, ctx->out->cur);
ctx->out->cur += strlen(ctx->out->cur);
} else if (SvIOKp(val)) {
fujson_fmt_int(aTHX_ ctx, val);
} else if (SvROK(val)) {
/* Simply consider every reference a form of nesting. TO_JSON may
* return a scalar, but it may also return another TO_JSON object and
* cause a stack overflow that way. */
if (--ctx->depth == 0) croak("max_depth exceeded while formatting JSON");
SV *rv = SvRV(val);
SvGETMAGIC(rv);
if (UNLIKELY(SvOBJECT(rv))) fujson_fmt_obj(aTHX_ ctx, val, rv);
else if (SvTYPE(rv) == SVt_PVHV) fujson_fmt_hv(aTHX_ ctx, (HV *)rv);
else if (SvTYPE(rv) == SVt_PVAV) fujson_fmt_av(aTHX_ ctx, (AV *)rv);
else croak("unable to format reference '%s' as JSON", SvPV_nolen(val));
ctx->depth++;
} else if (!SvOK(val)) {
fustr_write(ctx->out, "null", 4);
} else {
croak("unable to format unknown value '%s' as JSON", SvPV_nolen(val));
}
}
static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
I32 i = 1;
int encutf8 = 0;
char *arg;
SV *r;
fustr out;
fujson_fmt_ctx ctx;
out.maxlen = 0;
ctx.out = &out;
ctx.depth = 0;
ctx.pretty = INT_MIN;
ctx.canon = ctx.htmlsafe = 0;
while (i < argc) {
arg = SvPV_nolen(ST(i));
i++;
if (i == argc) croak("Odd name/value argument for json_format()");
r = ST(i);
i++;
if (strcmp(arg, "canonical") == 0) ctx.canon = SvTRUEx(r);
else if (strcmp(arg, "pretty") == 0) ctx.pretty = SvTRUEx(r) ? 0 : INT_MIN;
else if (strcmp(arg, "html_safe") == 0) ctx.htmlsafe = !!SvTRUEx(r);
else if (strcmp(arg, "utf8") == 0) encutf8 = SvTRUEx(r);
else if (strcmp(arg, "max_size") == 0) out.maxlen = SvUV(r);
else if (strcmp(arg, "max_depth") == 0) ctx.depth = SvUV(r);
else croak("Unknown flag: '%s'", arg);
}
if (out.maxlen == 0) out.maxlen = 1<<30;
if (ctx.depth == 0) ctx.depth = 512;
fustr_init(&out, sv_newmortal(), out.maxlen);
fujson_fmt(aTHX_ &ctx, val);
( run in 2.872 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )