jsonfmt: Add canonical option
Not as bad as I had expected it to be; managed to keep the implementation a little bit simpler and cleaner than JSON::XS.
This commit is contained in:
parent
163a60b4ba
commit
1a0fb03205
5 changed files with 115 additions and 23 deletions
68
c/jsonfmt.c
68
c/jsonfmt.c
|
|
@ -1,6 +1,7 @@
|
|||
typedef struct {
|
||||
fustr out;
|
||||
UV depth;
|
||||
int canon;
|
||||
} fujson_fmt_ctx;
|
||||
|
||||
static void fujson_fmt(pTHX_ fujson_fmt_ctx *, SV *);
|
||||
|
|
@ -126,19 +127,65 @@ static void fujson_fmt_av(pTHX_ fujson_fmt_ctx *ctx, AV *av) {
|
|||
fustr_write(&ctx->out, "]", 1);
|
||||
}
|
||||
|
||||
static int fujson_fmt_hvcmp(const void *pa, const void *pb) {
|
||||
dTHX;
|
||||
HE *a = *(HE **)pa;
|
||||
HE *b = *(HE **)pb;
|
||||
STRLEN alen, blen;
|
||||
char *astr = HePV(a, alen);
|
||||
char *bstr = HePV(b, blen);
|
||||
int autf = HeUTF8(a);
|
||||
int butf = HeUTF8(b);
|
||||
|
||||
if (autf == butf) {
|
||||
int cmp = memcmp(bstr, astr, alen < blen ? alen : blen);
|
||||
return cmp != 0 ? cmp : blen < alen ? -1 : blen == alen ? 0 : 1;
|
||||
}
|
||||
return autf ? bytes_cmp_utf8((const U8*)bstr, blen, (const U8*)astr, alen)
|
||||
: -bytes_cmp_utf8((const U8*)astr, alen, (const U8*)bstr, blen);
|
||||
}
|
||||
|
||||
static void fujson_fmt_hvkv(pTHX_ fujson_fmt_ctx *ctx, HV *hv, HE *he, char **hestr) {
|
||||
STRLEN helen;
|
||||
if (*hestr) fustr_write(&ctx->out, ",", 1);
|
||||
*hestr = HePV(he, helen);
|
||||
fujson_fmt_str(aTHX_ ctx, *hestr, helen, HeUTF8(he));
|
||||
fustr_write(&ctx->out, ":", 1);
|
||||
fujson_fmt(aTHX_ ctx, UNLIKELY(SvMAGICAL(hv)) ? hv_iterval(hv, he) : HeVAL(he));
|
||||
}
|
||||
|
||||
static void fujson_fmt_hv(pTHX_ fujson_fmt_ctx *ctx, HV *hv) {
|
||||
HE *he;
|
||||
STRLEN helen;
|
||||
char *hestr = NULL;
|
||||
|
||||
hv_iterinit(hv);
|
||||
int numkeys = hv_iterinit(hv);
|
||||
fustr_write(&ctx->out, "{", 1);
|
||||
while ((he = hv_iternext(hv))) {
|
||||
if (hestr) fustr_write(&ctx->out, ",", 1);
|
||||
hestr = HePV(he, helen);
|
||||
fujson_fmt_str(aTHX_ ctx, hestr, helen, HeUTF8(he));
|
||||
fustr_write(&ctx->out, ":", 1);
|
||||
fujson_fmt(aTHX_ ctx, UNLIKELY(SvMAGICAL(hv)) ? hv_iterval(hv, he) : HeVAL(he));
|
||||
|
||||
/* Canonical order on tied hashes is not supported. Cpanel::JSON::XS has
|
||||
* code to deal with that case and it's absolutely horrifying. */
|
||||
if (ctx->canon && !(SvMAGICAL(hv) && SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) {
|
||||
SAVETMPS;
|
||||
if (numkeys < 4) numkeys = 4;
|
||||
if (SvMAGICAL(hv)) numkeys = 32;
|
||||
|
||||
SV *keys_sv = sv_2mortal(newSV(numkeys * sizeof(HE*)));
|
||||
HE **keys = (HE **)SvPVX(keys_sv);
|
||||
int i = 0;
|
||||
|
||||
while ((he = hv_iternext(hv))) {
|
||||
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);
|
||||
}
|
||||
fustr_write(&ctx->out, "}", 1);
|
||||
}
|
||||
|
|
@ -222,6 +269,7 @@ static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
|
|||
|
||||
ctx.out.maxlen = 0;
|
||||
ctx.depth = 0;
|
||||
ctx.canon = 0;
|
||||
while (i < argc) {
|
||||
arg = SvPV_nolen(ST(i));
|
||||
i++;
|
||||
|
|
@ -229,7 +277,8 @@ static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
|
|||
r = ST(i);
|
||||
i++;
|
||||
|
||||
if (strcmp(arg, "utf8") == 0) encutf8 = SvPVXtrue(r);
|
||||
if (strcmp(arg, "canonical") == 0) ctx.canon = SvPVXtrue(r);
|
||||
else if (strcmp(arg, "utf8") == 0) encutf8 = SvPVXtrue(r);
|
||||
else if (strcmp(arg, "max_size") == 0) ctx.out.maxlen = SvUV(r);
|
||||
else if (strcmp(arg, "max_depth") == 0) ctx.depth = SvUV(r);
|
||||
else croak("Unknown flag: '%s'", arg);
|
||||
|
|
@ -244,5 +293,4 @@ static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) {
|
|||
return r;
|
||||
}
|
||||
|
||||
/* TODO: canonical */
|
||||
/* TODO: pretty */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue