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 )