1 /* MIT (BSD) license - see LICENSE file for details */
2 #include <ccan/json_escape/json_escape.h>
3 #include <ccan/json_out/json_out.h>
4 #include <ccan/membuf/membuf.h>
9 /* Callback if we reallocate. */
10 void (*move_cb)(struct json_out *jout, ptrdiff_t delta, void *arg);
13 #ifdef CCAN_JSON_OUT_DEBUG
14 /* tal_arr of types ( or [ we're enclosed in. */
17 /* True if we haven't yet put an element in current wrapping */
24 /* Realloc helper for tal membufs */
25 static void *membuf_tal_realloc(struct membuf *mb,
26 void *rawelems, size_t newsize)
30 tal_resize(&p, newsize);
34 struct json_out *json_out_new(const tal_t *ctx)
36 struct json_out *jout = tal(ctx, struct json_out);
38 membuf_init(&jout->outbuf,
39 tal_arr(jout, char, 64), 64, membuf_tal_realloc);
40 #ifdef CCAN_JSON_OUT_DEBUG
41 jout->wrapping = tal_arr(jout, char, 0);
48 void json_out_call_on_move_(struct json_out *jout,
49 void (*cb)(struct json_out *jout, ptrdiff_t delta,
54 assert(!jout->move_cb);
59 struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
61 size_t num_elems = membuf_num_elems(&src->outbuf);
62 char *elems = membuf_elems(&src->outbuf);
63 struct json_out *jout = tal_dup(ctx, struct json_out, src);
64 membuf_init(&jout->outbuf,
65 tal_dup_arr(jout, char, elems, num_elems, 0),
66 num_elems, membuf_tal_realloc);
67 membuf_added(&jout->outbuf, num_elems);
68 #ifdef CCAN_JSON_OUT_DEBUG
69 jout->wrapping = tal_dup_arr(jout, char,
70 jout->wrapping, tal_count(jout->wrapping),
76 static void indent(struct json_out *jout, char type)
78 #ifdef CCAN_JSON_OUT_DEBUG
79 size_t n = tal_count(jout->wrapping);
80 tal_resize(&jout->wrapping, n+1);
81 jout->wrapping[n] = type;
86 static void unindent(struct json_out *jout, char type)
88 #ifdef CCAN_JSON_OUT_DEBUG
89 size_t indent = tal_count(jout->wrapping);
91 /* Both [ and ] and { and } are two apart in ASCII */
92 assert(jout->wrapping[indent-1] == type - 2);
93 tal_resize(&jout->wrapping, indent-1);
98 /* Make sure jout->outbuf has room for len: return pointer */
99 static char *mkroom(struct json_out *jout, size_t len)
101 ptrdiff_t delta = membuf_prepare_space(&jout->outbuf, len);
103 if (delta && jout->move_cb)
104 jout->move_cb(jout, delta, jout->cb_arg);
106 return membuf_space(&jout->outbuf);
109 static void check_fieldname(const struct json_out *jout,
110 const char *fieldname)
112 #ifdef CCAN_JSON_OUT_DEBUG
113 size_t n = tal_count(jout->wrapping);
115 /* Can't have a fieldname if not in anything! */
117 else if (jout->wrapping[n-1] == '[')
118 /* No fieldnames in arrays. */
121 /* Must have fieldnames in objects. */
123 /* We don't escape this for you */
124 assert(!json_escape_needed(fieldname, strlen(fieldname)));
129 char *json_out_member_direct(struct json_out *jout,
130 const char *fieldname, size_t extra)
134 /* Prepend comma if required. */
138 check_fieldname(jout, fieldname);
140 extra += 1 + strlen(fieldname) + 2;
147 dest = mkroom(jout, extra);
153 memcpy(dest, fieldname, strlen(fieldname));
154 dest += strlen(fieldname);
158 membuf_added(&jout->outbuf, extra);
165 void json_out_start(struct json_out *jout, const char *fieldname, char type)
167 assert(type == '[' || type == '{');
168 json_out_member_direct(jout, fieldname, 1)[0] = type;
172 void json_out_end(struct json_out *jout, char type)
174 assert(type == '}' || type == ']');
175 json_out_direct(jout, 1)[0] = type;
176 unindent(jout, type);
179 void json_out_addv(struct json_out *jout,
180 const char *fieldname,
185 size_t fmtlen, avail;
189 json_out_member_direct(jout, fieldname, 0);
191 /* Make a copy in case we need it below. */
194 /* We can use any additional space, but need room for ". */
195 avail = membuf_num_space(&jout->outbuf);
203 /* Try printing in place first. */
204 dst = membuf_space(&jout->outbuf);
205 fmtlen = vsnprintf(dst + quote, avail, fmt, ap);
207 /* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means
208 * chopping off the last character. So if fmtlen ==
209 * membuf_num_space(&jout->outbuf), the result was truncated! */
210 if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) {
211 /* Make room for NUL terminator, even though we don't want it */
212 dst = mkroom(jout, fmtlen + 1 + (int)quote*2);
213 vsprintf(dst + quote, fmt, ap2);
216 /* Of course, if we need to escape it, we have to redo it all. */
217 if (json_escape_needed(dst + quote, fmtlen)) {
218 struct json_escape *e;
219 e = json_escape_len(NULL, dst + quote, fmtlen);
220 fmtlen = strlen(e->s);
221 dst = mkroom(jout, fmtlen + (int)quote*2);
222 memcpy(dst + quote, e, fmtlen);
230 membuf_added(&jout->outbuf, fmtlen + (int)quote*2);
234 void json_out_add(struct json_out *jout,
235 const char *fieldname,
237 const char *fmt, ...)
242 json_out_addv(jout, fieldname, quote, fmt, ap);
246 void json_out_addstr(struct json_out *jout,
247 const char *fieldname,
250 size_t len = strlen(str);
253 p = json_out_member_direct(jout, fieldname, len + 2);
254 p[0] = p[1+len] = '"';
255 memcpy(p+1, str, len);
258 void json_out_add_splice(struct json_out *jout,
259 const char *fieldname,
260 const struct json_out *src)
265 p = json_out_contents(src, &len);
266 memcpy(json_out_member_direct(jout, fieldname, len), p, len);
269 char *json_out_direct(struct json_out *jout, size_t len)
271 char *p = mkroom(jout, len);
272 membuf_added(&jout->outbuf, len);
276 void json_out_finished(const struct json_out *jout)
278 #ifdef CCAN_JSON_OUT_DEBUG
279 assert(tal_count(jout->wrapping) == 0);
283 const char *json_out_contents(const struct json_out *jout, size_t *len)
285 *len = membuf_num_elems(&jout->outbuf);
286 return *len ? membuf_elems(&jout->outbuf) : NULL;
289 void json_out_consume(struct json_out *jout, size_t len)
291 membuf_consume(&jout->outbuf, len);