| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
typedef struct { |
|
2
|
|
|
|
|
|
|
fustr *out; |
|
3
|
|
|
|
|
|
|
UV depth; |
|
4
|
|
|
|
|
|
|
int canon; |
|
5
|
|
|
|
|
|
|
int pretty; /* <0 when disabled, current nesting level otherwise */ |
|
6
|
|
|
|
|
|
|
int htmlsafe; |
|
7
|
|
|
|
|
|
|
} fujson_fmt_ctx; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
static void fujson_fmt(pTHX_ fujson_fmt_ctx *, SV *); |
|
10
|
|
|
|
|
|
|
|
|
11
|
2135
|
|
|
|
|
|
static void fujson_fmt_indent(pTHX_ fujson_fmt_ctx *ctx) { |
|
12
|
2135
|
100
|
|
|
|
|
if (ctx->pretty >= 0) { |
|
13
|
15
|
|
|
|
|
|
char *buf = fustr_write_buf(ctx->out, 1 + ctx->pretty*3); |
|
14
|
15
|
|
|
|
|
|
*buf = '\n'; |
|
15
|
15
|
|
|
|
|
|
memset(buf+1, ' ', ctx->pretty*3); |
|
16
|
|
|
|
|
|
|
} |
|
17
|
2135
|
|
|
|
|
|
} |
|
18
|
|
|
|
|
|
|
|
|
19
|
1211
|
|
|
|
|
|
static void fujson_fmt_str(pTHX_ fujson_fmt_ctx *ctx, const char *stri, size_t len, int utf8) { |
|
20
|
1211
|
|
|
|
|
|
size_t off = 0, loff; |
|
21
|
1211
|
|
|
|
|
|
const unsigned char *str = (const unsigned char *)stri; |
|
22
|
|
|
|
|
|
|
unsigned char *buf; |
|
23
|
1211
|
|
|
|
|
|
unsigned char x = 0; |
|
24
|
|
|
|
|
|
|
|
|
25
|
1211
|
|
|
|
|
|
fustr_write_ch(ctx->out, '\"'); |
|
26
|
1211
|
|
|
|
|
|
fustr_reserve(ctx->out, len); |
|
27
|
|
|
|
|
|
|
|
|
28
|
1937
|
100
|
|
|
|
|
while (off < len) { |
|
29
|
|
|
|
|
|
|
/* Fast path: no escaping needed */ |
|
30
|
727
|
|
|
|
|
|
loff = off; |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
#define SKIPUNTIL(cond) \ |
|
33
|
|
|
|
|
|
|
while (off < len) { \ |
|
34
|
|
|
|
|
|
|
x = str[off]; \ |
|
35
|
|
|
|
|
|
|
if (x <= 0x1f || x == '"' || x == '\\' || cond) break; \ |
|
36
|
|
|
|
|
|
|
off++;\ |
|
37
|
|
|
|
|
|
|
} |
|
38
|
727
|
100
|
|
|
|
|
if (utf8) { |
|
39
|
47
|
50
|
|
|
|
|
if (!ctx->htmlsafe) { SKIPUNTIL(x == 0x7f) } |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
40
|
0
|
0
|
|
|
|
|
else { SKIPUNTIL(x == 0x7f || x == '<' || x == '>' || x == '&') } |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
} else { |
|
42
|
4743859
|
100
|
|
|
|
|
if (!ctx->htmlsafe) { SKIPUNTIL(x >= 0x7f) } |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
43
|
15
|
50
|
|
|
|
|
else { SKIPUNTIL(x >= 0x7f || x == '<' || x == '>' || x == '&') } |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
#undef SKIPUNTIL |
|
46
|
|
|
|
|
|
|
|
|
47
|
727
|
|
|
|
|
|
fustr_write(ctx->out, (char *)str+loff, off-loff); |
|
48
|
|
|
|
|
|
|
|
|
49
|
727
|
100
|
|
|
|
|
if (off < len) { /* early break, which means current byte needs special processing */ |
|
50
|
24
|
|
|
|
|
|
switch (x) { |
|
51
|
0
|
|
|
|
|
|
case '"': fustr_write(ctx->out, "\\\"", 2); break; |
|
52
|
0
|
|
|
|
|
|
case '\\': fustr_write(ctx->out, "\\\\", 2); break; |
|
53
|
0
|
|
|
|
|
|
case 0x08: fustr_write(ctx->out, "\\b", 2); break; |
|
54
|
0
|
|
|
|
|
|
case 0x09: fustr_write(ctx->out, "\\t", 2); break; |
|
55
|
4
|
|
|
|
|
|
case 0x0a: fustr_write(ctx->out, "\\n", 2); break; |
|
56
|
0
|
|
|
|
|
|
case 0x0c: fustr_write(ctx->out, "\\f", 2); break; |
|
57
|
4
|
|
|
|
|
|
case 0x0d: fustr_write(ctx->out, "\\r", 2); break; |
|
58
|
16
|
|
|
|
|
|
default: |
|
59
|
16
|
100
|
|
|
|
|
if (x < 0x80) { |
|
60
|
9
|
|
|
|
|
|
buf = (unsigned char *)fustr_write_buf(ctx->out, 6); |
|
61
|
9
|
|
|
|
|
|
memcpy(buf, "\\u00", 4); |
|
62
|
9
|
|
|
|
|
|
buf[4] = PL_hexdigit[(x >> 4) & 0x0f]; |
|
63
|
9
|
|
|
|
|
|
buf[5] = PL_hexdigit[x & 0x0f]; |
|
64
|
|
|
|
|
|
|
} else { /* x >= 0x80, !utf8, so encode as 2-byte UTF-8 */ |
|
65
|
7
|
|
|
|
|
|
buf = (unsigned char *)fustr_write_buf(ctx->out, 2); |
|
66
|
7
|
|
|
|
|
|
buf[0] = 0xc0 | (x >> 6); |
|
67
|
7
|
|
|
|
|
|
buf[1] = 0x80 | (x & 0x3f); |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
} |
|
70
|
24
|
|
|
|
|
|
off++; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
1210
|
|
|
|
|
|
fustr_write_ch(ctx->out, '\"'); |
|
75
|
1210
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
static const char fujson_digits[] = |
|
78
|
|
|
|
|
|
|
"00010203040506070809" |
|
79
|
|
|
|
|
|
|
"10111213141516171819" |
|
80
|
|
|
|
|
|
|
"20212223242526272829" |
|
81
|
|
|
|
|
|
|
"30313233343536373839" |
|
82
|
|
|
|
|
|
|
"40414243444546474849" |
|
83
|
|
|
|
|
|
|
"50515253545556575859" |
|
84
|
|
|
|
|
|
|
"60616263646566676869" |
|
85
|
|
|
|
|
|
|
"70717273747576777879" |
|
86
|
|
|
|
|
|
|
"80818283848586878889" |
|
87
|
|
|
|
|
|
|
"90919293949596979899"; |
|
88
|
|
|
|
|
|
|
|
|
89
|
4074
|
|
|
|
|
|
static void fujson_fmt_int(pTHX_ fujson_fmt_ctx *ctx, SV *val) { |
|
90
|
|
|
|
|
|
|
char buf[32]; |
|
91
|
4074
|
|
|
|
|
|
char *r = buf+31; |
|
92
|
4074
|
|
|
|
|
|
int neg = 0; |
|
93
|
|
|
|
|
|
|
IV iv; |
|
94
|
|
|
|
|
|
|
UV uv; |
|
95
|
|
|
|
|
|
|
|
|
96
|
4074
|
100
|
|
|
|
|
if (SvIsUV(val)) { /* Why is this macro not documented? */ |
|
97
|
2
|
|
|
|
|
|
uv = SvUV_nomg(val); |
|
98
|
|
|
|
|
|
|
} else { |
|
99
|
4072
|
|
|
|
|
|
iv = SvIV_nomg(val); |
|
100
|
4072
|
|
|
|
|
|
neg = iv < 0; |
|
101
|
4072
|
100
|
|
|
|
|
uv = neg ? -iv : iv; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
4074
|
100
|
|
|
|
|
if (uv == 0) { |
|
105
|
9
|
|
|
|
|
|
fustr_write_ch(ctx->out, '0'); |
|
106
|
9
|
|
|
|
|
|
return; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
|
|
109
|
8156
|
100
|
|
|
|
|
while (uv >= 10) { |
|
110
|
4091
|
|
|
|
|
|
r -= 2; |
|
111
|
4091
|
|
|
|
|
|
memcpy(r, fujson_digits + ((uv % 100)<<1), 2); |
|
112
|
4091
|
|
|
|
|
|
uv /= 100; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
4065
|
100
|
|
|
|
|
if (uv > 0) *(--r) = '0' + (uv % 10); |
|
115
|
4065
|
100
|
|
|
|
|
if (neg) *(--r) = '-'; |
|
116
|
4065
|
|
|
|
|
|
fustr_write(ctx->out, r, 31 - (r - buf)); |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
532
|
|
|
|
|
|
static void fujson_fmt_av(pTHX_ fujson_fmt_ctx *ctx, AV *av) { |
|
120
|
532
|
|
|
|
|
|
int i, len = av_count(av); |
|
121
|
532
|
|
|
|
|
|
fustr_write_ch(ctx->out, '['); |
|
122
|
532
|
|
|
|
|
|
ctx->pretty++; |
|
123
|
1083
|
100
|
|
|
|
|
for (i=0; i
|
|
124
|
552
|
100
|
|
|
|
|
if (i) fustr_write_ch(ctx->out, ','); |
|
125
|
552
|
|
|
|
|
|
fujson_fmt_indent(aTHX_ ctx); |
|
126
|
552
|
|
|
|
|
|
SV **sv = av_fetch(av, i, 0); |
|
127
|
552
|
50
|
|
|
|
|
if (sv) fujson_fmt(aTHX_ ctx, *sv); /* sv will have magic if av is tied, but fujson_fmt() handles that. */ |
|
128
|
0
|
|
|
|
|
|
else fustr_write(ctx->out, "null", 4); |
|
129
|
|
|
|
|
|
|
} |
|
130
|
531
|
|
|
|
|
|
ctx->pretty--; |
|
131
|
531
|
100
|
|
|
|
|
if (i) fujson_fmt_indent(aTHX_ ctx); |
|
132
|
531
|
|
|
|
|
|
fustr_write_ch(ctx->out, ']'); |
|
133
|
531
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
63
|
|
|
|
|
|
static int fujson_fmt_hvcmp(const void *pa, const void *pb) { |
|
136
|
|
|
|
|
|
|
dTHX; |
|
137
|
63
|
|
|
|
|
|
HE *a = *(HE **)pa; |
|
138
|
63
|
|
|
|
|
|
HE *b = *(HE **)pb; |
|
139
|
|
|
|
|
|
|
STRLEN alen, blen; |
|
140
|
63
|
50
|
|
|
|
|
char *astr = HePV(a, alen); |
|
141
|
63
|
50
|
|
|
|
|
char *bstr = HePV(b, blen); |
|
142
|
63
|
50
|
|
|
|
|
int autf = HeUTF8(a); |
|
143
|
63
|
50
|
|
|
|
|
int butf = HeUTF8(b); |
|
144
|
|
|
|
|
|
|
|
|
145
|
63
|
100
|
|
|
|
|
if (autf == butf) { |
|
146
|
58
|
|
|
|
|
|
int cmp = memcmp(bstr, astr, alen < blen ? alen : blen); |
|
147
|
58
|
100
|
|
|
|
|
return cmp != 0 ? cmp : blen < alen ? -1 : blen == alen ? 0 : 1; |
|
|
|
100
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
} |
|
149
|
2
|
|
|
|
|
|
return autf ? bytes_cmp_utf8((const U8*)bstr, blen, (const U8*)astr, alen) |
|
150
|
7
|
100
|
|
|
|
|
: -bytes_cmp_utf8((const U8*)astr, alen, (const U8*)bstr, blen); |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
546
|
|
|
|
|
|
static void fujson_fmt_hvkv(pTHX_ fujson_fmt_ctx *ctx, HV *hv, HE *he, char **hestr) { |
|
154
|
|
|
|
|
|
|
STRLEN helen; |
|
155
|
546
|
100
|
|
|
|
|
if (*hestr) fustr_write_ch(ctx->out, ','); |
|
156
|
546
|
|
|
|
|
|
fujson_fmt_indent(aTHX_ ctx); |
|
157
|
546
|
100
|
|
|
|
|
*hestr = HePV(he, helen); |
|
158
|
546
|
100
|
|
|
|
|
fujson_fmt_str(aTHX_ ctx, *hestr, helen, HeUTF8(he)); |
|
159
|
546
|
100
|
|
|
|
|
if (ctx->pretty > 0) fustr_write(ctx->out, " : ", 3); |
|
160
|
538
|
|
|
|
|
|
else fustr_write_ch(ctx->out, ':'); |
|
161
|
546
|
100
|
|
|
|
|
fujson_fmt(aTHX_ ctx, UNLIKELY(SvMAGICAL(hv)) ? hv_iterval(hv, he) : HeVAL(he)); |
|
162
|
546
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
517
|
|
|
|
|
|
static void fujson_fmt_hv(pTHX_ fujson_fmt_ctx *ctx, HV *hv) { |
|
165
|
|
|
|
|
|
|
HE *he; |
|
166
|
517
|
|
|
|
|
|
char *hestr = NULL; |
|
167
|
|
|
|
|
|
|
|
|
168
|
517
|
|
|
|
|
|
int numkeys = hv_iterinit(hv); |
|
169
|
517
|
|
|
|
|
|
fustr_write_ch(ctx->out, '{'); |
|
170
|
517
|
|
|
|
|
|
ctx->pretty++; |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
/* Canonical order on tied hashes is not supported. Cpanel::JSON::XS has |
|
173
|
|
|
|
|
|
|
* code to deal with that case and it's absolutely horrifying. */ |
|
174
|
526
|
100
|
|
|
|
|
if (ctx->canon && !(SvMAGICAL(hv) && SvTIED_mg((SV*)hv, PERL_MAGIC_tied))) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
175
|
9
|
|
|
|
|
|
SAVETMPS; |
|
176
|
9
|
100
|
|
|
|
|
if (numkeys < 4) numkeys = 4; |
|
177
|
9
|
50
|
|
|
|
|
if (SvMAGICAL(hv)) numkeys = 32; |
|
178
|
|
|
|
|
|
|
|
|
179
|
9
|
|
|
|
|
|
SV *keys_sv = sv_2mortal(newSV(numkeys * sizeof(HE*))); |
|
180
|
9
|
|
|
|
|
|
HE **keys = (HE **)SvPVX(keys_sv); |
|
181
|
9
|
|
|
|
|
|
int i = 0; |
|
182
|
|
|
|
|
|
|
|
|
183
|
45
|
100
|
|
|
|
|
while ((he = hv_iternext(hv))) { |
|
184
|
36
|
50
|
|
|
|
|
if (i >= numkeys) { |
|
185
|
0
|
|
|
|
|
|
numkeys += numkeys >> 1; |
|
186
|
0
|
0
|
|
|
|
|
keys = (HE **)SvGROW(keys_sv, numkeys * sizeof(HE*)); |
|
|
|
0
|
|
|
|
|
|
|
187
|
0
|
|
|
|
|
|
numkeys = SvLEN(keys_sv) / sizeof(HE*); |
|
188
|
|
|
|
|
|
|
} |
|
189
|
36
|
|
|
|
|
|
keys[i++] = he; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
9
|
|
|
|
|
|
qsort(keys, i, sizeof(HE *), fujson_fmt_hvcmp); |
|
192
|
45
|
100
|
|
|
|
|
while (i--) fujson_fmt_hvkv(aTHX_ ctx, hv, keys[i], &hestr); |
|
193
|
9
|
100
|
|
|
|
|
FREETMPS; |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
} else { |
|
196
|
1018
|
100
|
|
|
|
|
while ((he = hv_iternext(hv))) fujson_fmt_hvkv(aTHX_ ctx, hv, he, &hestr); |
|
197
|
|
|
|
|
|
|
} |
|
198
|
517
|
|
|
|
|
|
ctx->pretty--; |
|
199
|
517
|
100
|
|
|
|
|
if (hestr) fujson_fmt_indent(aTHX_ ctx); |
|
200
|
517
|
|
|
|
|
|
fustr_write_ch(ctx->out, '}'); |
|
201
|
517
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
8
|
|
|
|
|
|
static void fujson_fmt_obj(pTHX_ fujson_fmt_ctx *ctx, SV *rv, SV *obj) { |
|
204
|
8
|
|
|
|
|
|
dSP; |
|
205
|
|
|
|
|
|
|
|
|
206
|
8
|
|
|
|
|
|
GV *method = gv_fetchmethod_autoload(SvSTASH(obj), "TO_JSON", 0); |
|
207
|
8
|
100
|
|
|
|
|
if (!method) croak("unable to format '%s' object as JSON", HvNAME(SvSTASH(obj))); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
|
|
209
|
7
|
|
|
|
|
|
ENTER; |
|
210
|
7
|
|
|
|
|
|
SAVETMPS; |
|
211
|
|
|
|
|
|
|
|
|
212
|
7
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
213
|
7
|
50
|
|
|
|
|
XPUSHs(rv); |
|
214
|
|
|
|
|
|
|
|
|
215
|
7
|
|
|
|
|
|
PUTBACK; |
|
216
|
7
|
|
|
|
|
|
call_sv((SV *)GvCV(method), G_SCALAR); |
|
217
|
7
|
|
|
|
|
|
SPAGAIN; |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
/* JSON::XS describes this error as "surprisingly common"... I'd be |
|
220
|
|
|
|
|
|
|
* surprised indeed if it happens at all, but I suppose it can't hurt to |
|
221
|
|
|
|
|
|
|
* copy their check; this sounds like be a pain to debug otherwise. */ |
|
222
|
7
|
50
|
|
|
|
|
if (SvROK(TOPs) && SvRV(TOPs) == obj) |
|
|
|
100
|
|
|
|
|
|
|
223
|
1
|
50
|
|
|
|
|
croak("%s::TO_JSON method returned same object as was passed instead of a new one", HvNAME(SvSTASH(obj))); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
|
225
|
6
|
|
|
|
|
|
obj = POPs; |
|
226
|
6
|
|
|
|
|
|
PUTBACK; |
|
227
|
6
|
|
|
|
|
|
fujson_fmt(aTHX_ ctx, obj); |
|
228
|
|
|
|
|
|
|
|
|
229
|
6
|
50
|
|
|
|
|
FREETMPS; |
|
230
|
6
|
|
|
|
|
|
LEAVE; |
|
231
|
6
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
5839
|
|
|
|
|
|
static void fujson_fmt(pTHX_ fujson_fmt_ctx *ctx, SV *val) { |
|
234
|
5839
|
|
|
|
|
|
SvGETMAGIC(val); |
|
235
|
|
|
|
|
|
|
|
|
236
|
5839
|
|
|
|
|
|
int r = fu_2bool(aTHX_ val); |
|
237
|
5839
|
100
|
|
|
|
|
if (r != -1) { /* Must check SvISBOOL() before IOKp & POKp, because it implies both flags */ |
|
238
|
15
|
100
|
|
|
|
|
if (r) fustr_write(ctx->out, "true", 4); |
|
239
|
5
|
|
|
|
|
|
else fustr_write(ctx->out, "false", 5); |
|
240
|
5824
|
100
|
|
|
|
|
} else if (SvPOKp(val)) { |
|
241
|
665
|
|
|
|
|
|
fujson_fmt_str(aTHX_ ctx, SvPVX(val), SvCUR(val), SvUTF8(val)); |
|
242
|
5159
|
100
|
|
|
|
|
} else if (SvNOKp(val)) { /* Must check before IOKp, because integer conversion might have been lossy */ |
|
243
|
15
|
|
|
|
|
|
NV nv = SvNV_nomg(val); |
|
244
|
15
|
100
|
|
|
|
|
if (isinfnan(nv)) croak("unable to format floating point NaN or Inf as JSON"); |
|
245
|
|
|
|
|
|
|
/* XXX: Cpanel::JSON::XS appears to always append a ".0" for round numbers, other modules do not. */ |
|
246
|
|
|
|
|
|
|
/* XXX#2: This doesn't support quadmath. Makefile.PL checks for that */ |
|
247
|
13
|
|
|
|
|
|
fustr_reserve(ctx->out, NV_DIG+32); |
|
248
|
13
|
|
|
|
|
|
Gconvert(nv, NV_DIG, 0, ctx->out->cur); |
|
249
|
13
|
|
|
|
|
|
ctx->out->cur += strlen(ctx->out->cur); |
|
250
|
5144
|
100
|
|
|
|
|
} else if (SvIOKp(val)) { |
|
251
|
4074
|
|
|
|
|
|
fujson_fmt_int(aTHX_ ctx, val); |
|
252
|
1070
|
100
|
|
|
|
|
} else if (SvROK(val)) { |
|
253
|
|
|
|
|
|
|
/* Simply consider every reference a form of nesting. TO_JSON may |
|
254
|
|
|
|
|
|
|
* return a scalar, but it may also return another TO_JSON object and |
|
255
|
|
|
|
|
|
|
* cause a stack overflow that way. */ |
|
256
|
1059
|
100
|
|
|
|
|
if (--ctx->depth == 0) croak("max_depth exceeded while formatting JSON"); |
|
257
|
1058
|
|
|
|
|
|
SV *rv = SvRV(val); |
|
258
|
1058
|
|
|
|
|
|
SvGETMAGIC(rv); |
|
259
|
1058
|
100
|
|
|
|
|
if (UNLIKELY(SvOBJECT(rv))) fujson_fmt_obj(aTHX_ ctx, val, rv); |
|
260
|
1050
|
100
|
|
|
|
|
else if (SvTYPE(rv) == SVt_PVHV) fujson_fmt_hv(aTHX_ ctx, (HV *)rv); |
|
261
|
533
|
100
|
|
|
|
|
else if (SvTYPE(rv) == SVt_PVAV) fujson_fmt_av(aTHX_ ctx, (AV *)rv); |
|
262
|
1
|
|
|
|
|
|
else croak("unable to format reference '%s' as JSON", SvPV_nolen(val)); |
|
263
|
1054
|
|
|
|
|
|
ctx->depth++; |
|
264
|
11
|
100
|
|
|
|
|
} else if (!SvOK(val)) { |
|
265
|
10
|
|
|
|
|
|
fustr_write(ctx->out, "null", 4); |
|
266
|
|
|
|
|
|
|
} else { |
|
267
|
1
|
|
|
|
|
|
croak("unable to format unknown value '%s' as JSON", SvPV_nolen(val)); |
|
268
|
|
|
|
|
|
|
} |
|
269
|
5830
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
|
|
272
|
4735
|
|
|
|
|
|
static SV *fujson_fmt_xs(pTHX_ I32 ax, I32 argc, SV *val) { |
|
273
|
4735
|
|
|
|
|
|
I32 i = 1; |
|
274
|
4735
|
|
|
|
|
|
int encutf8 = 0; |
|
275
|
|
|
|
|
|
|
char *arg; |
|
276
|
|
|
|
|
|
|
SV *r; |
|
277
|
|
|
|
|
|
|
fustr out; |
|
278
|
|
|
|
|
|
|
fujson_fmt_ctx ctx; |
|
279
|
|
|
|
|
|
|
|
|
280
|
4735
|
|
|
|
|
|
out.maxlen = 0; |
|
281
|
4735
|
|
|
|
|
|
ctx.out = &out; |
|
282
|
4735
|
|
|
|
|
|
ctx.depth = 0; |
|
283
|
4735
|
|
|
|
|
|
ctx.pretty = INT_MIN; |
|
284
|
4735
|
|
|
|
|
|
ctx.canon = ctx.htmlsafe = 0; |
|
285
|
6785
|
100
|
|
|
|
|
while (i < argc) { |
|
286
|
2050
|
|
|
|
|
|
arg = SvPV_nolen(ST(i)); |
|
287
|
2050
|
|
|
|
|
|
i++; |
|
288
|
2050
|
50
|
|
|
|
|
if (i == argc) croak("Odd name/value argument for json_format()"); |
|
289
|
2050
|
|
|
|
|
|
r = ST(i); |
|
290
|
2050
|
|
|
|
|
|
i++; |
|
291
|
|
|
|
|
|
|
|
|
292
|
2050
|
100
|
|
|
|
|
if (strcmp(arg, "canonical") == 0) ctx.canon = SvTRUEx(r); |
|
293
|
2045
|
100
|
|
|
|
|
else if (strcmp(arg, "pretty") == 0) ctx.pretty = SvTRUEx(r) ? 0 : INT_MIN; |
|
|
|
50
|
|
|
|
|
|
|
294
|
2044
|
100
|
|
|
|
|
else if (strcmp(arg, "html_safe") == 0) ctx.htmlsafe = !!SvTRUEx(r); |
|
295
|
2043
|
100
|
|
|
|
|
else if (strcmp(arg, "utf8") == 0) encutf8 = SvTRUEx(r); |
|
296
|
2
|
100
|
|
|
|
|
else if (strcmp(arg, "max_size") == 0) out.maxlen = SvUV(r); |
|
297
|
1
|
50
|
|
|
|
|
else if (strcmp(arg, "max_depth") == 0) ctx.depth = SvUV(r); |
|
298
|
0
|
|
|
|
|
|
else croak("Unknown flag: '%s'", arg); |
|
299
|
|
|
|
|
|
|
} |
|
300
|
4735
|
100
|
|
|
|
|
if (out.maxlen == 0) out.maxlen = 1<<30; |
|
301
|
4735
|
100
|
|
|
|
|
if (ctx.depth == 0) ctx.depth = 512; |
|
302
|
|
|
|
|
|
|
|
|
303
|
4735
|
|
|
|
|
|
fustr_init(&out, sv_newmortal(), out.maxlen); |
|
304
|
4735
|
|
|
|
|
|
fujson_fmt(aTHX_ &ctx, val); |
|
305
|
4727
|
100
|
|
|
|
|
if (ctx.pretty >= 0) fustr_write_ch(&out, '\n'); |
|
306
|
4727
|
|
|
|
|
|
r = fustr_done(&out); |
|
307
|
4727
|
100
|
|
|
|
|
if (!encutf8) SvUTF8_on(r); |
|
308
|
4727
|
|
|
|
|
|
return r; |
|
309
|
|
|
|
|
|
|
} |