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. NULL if oom. */
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 if (!tal_resize(&p, newsize))
35 struct json_out *json_out_new(const tal_t *ctx)
37 struct json_out *jout = tal(ctx, struct json_out);
42 pool = tal_arr(jout, char, 64);
44 return tal_free(jout);
46 membuf_init(&jout->outbuf, pool, tal_count(pool), membuf_tal_realloc);
47 #ifdef CCAN_JSON_OUT_DEBUG
48 jout->wrapping = tal_arr(jout, char, 0);
55 void json_out_call_on_move_(struct json_out *jout,
56 void (*cb)(struct json_out *jout, ptrdiff_t delta,
61 assert(!jout->move_cb);
66 struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
68 size_t num_elems = membuf_num_elems(&src->outbuf);
69 char *elems = membuf_elems(&src->outbuf);
70 struct json_out *jout = tal_dup(ctx, struct json_out, src);
75 pool = tal_dup_arr(jout, char, elems, num_elems, 0);
77 return tal_free(jout);
78 membuf_init(&jout->outbuf, pool, num_elems, membuf_tal_realloc);
79 membuf_added(&jout->outbuf, num_elems);
80 #ifdef CCAN_JSON_OUT_DEBUG
81 jout->wrapping = tal_dup_arr(jout, char,
82 jout->wrapping, tal_count(jout->wrapping),
88 static void indent(struct json_out *jout, char type)
90 #ifdef CCAN_JSON_OUT_DEBUG
91 /* Can't check if we ran out of memory. */
93 size_t n = tal_count(jout->wrapping);
94 if (!tal_resize(&jout->wrapping, n+1))
95 jout->wrapping = tal_free(jout->wrapping);
97 jout->wrapping[n] = type;
103 static void unindent(struct json_out *jout, char type)
105 #ifdef CCAN_JSON_OUT_DEBUG
106 /* Can't check if we ran out of memory. */
107 if (jout->wrapping) {
108 size_t indent = tal_count(jout->wrapping);
110 /* Both [ and ] and { and } are two apart in ASCII */
111 assert(jout->wrapping[indent-1] == type - 2);
112 tal_resize(&jout->wrapping, indent-1);
118 /* Make sure jout->outbuf has room for len: return pointer */
119 static char *mkroom(struct json_out *jout, size_t len)
121 ptrdiff_t delta = membuf_prepare_space(&jout->outbuf, len);
123 if (delta && jout->move_cb)
124 jout->move_cb(jout, delta, jout->cb_arg);
126 return membuf_space(&jout->outbuf);
129 static void check_fieldname(const struct json_out *jout,
130 const char *fieldname)
132 #ifdef CCAN_JSON_OUT_DEBUG
133 /* We don't escape this for you */
134 assert(!fieldname || !json_escape_needed(fieldname, strlen(fieldname)));
136 /* Can't check anything else if we ran out of memory. */
137 if (jout->wrapping) {
138 size_t n = tal_count(jout->wrapping);
140 /* Can't have a fieldname if not in anything! */
142 else if (jout->wrapping[n-1] == '[')
143 /* No fieldnames in arrays. */
146 /* Must have fieldnames in objects. */
153 char *json_out_member_direct(struct json_out *jout,
154 const char *fieldname, size_t extra)
158 /* Prepend comma if required. */
162 check_fieldname(jout, fieldname);
164 extra += 1 + strlen(fieldname) + 2;
166 dest = mkroom(jout, extra);
174 memcpy(dest, fieldname, strlen(fieldname));
175 dest += strlen(fieldname);
179 membuf_added(&jout->outbuf, extra);
186 bool json_out_start(struct json_out *jout, const char *fieldname, char type)
190 assert(type == '[' || type == '{');
191 p = json_out_member_direct(jout, fieldname, 1);
199 bool json_out_end(struct json_out *jout, char type)
203 assert(type == '}' || type == ']');
204 p = json_out_direct(jout, 1);
207 unindent(jout, type);
212 bool json_out_addv(struct json_out *jout,
213 const char *fieldname,
218 size_t fmtlen, avail;
222 if (!json_out_member_direct(jout, fieldname, 0))
225 /* Make a copy in case we need it below. */
228 /* We can use any additional space, but need room for ". */
229 avail = membuf_num_space(&jout->outbuf);
237 /* Try printing in place first. */
238 dst = membuf_space(&jout->outbuf);
239 fmtlen = vsnprintf(dst + quote, avail, fmt, ap);
241 /* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means
242 * chopping off the last character. So if fmtlen ==
243 * membuf_num_space(&jout->outbuf), the result was truncated! */
244 if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) {
245 /* Make room for NUL terminator, even though we don't want it */
246 dst = mkroom(jout, fmtlen + 1 + (int)quote*2);
249 vsprintf(dst + quote, fmt, ap2);
252 #ifdef CCAN_JSON_OUT_DEBUG
253 /* You're not inserting junk here, are you? */
254 assert(quote || !json_escape_needed(dst, fmtlen));
257 /* Of course, if we need to escape it, we have to redo it all. */
259 if (json_escape_needed(dst + quote, fmtlen)) {
260 struct json_escape *e;
261 e = json_escape_len(NULL, dst + quote, fmtlen);
262 fmtlen = strlen(e->s);
263 dst = mkroom(jout, fmtlen + (int)quote*2);
266 memcpy(dst + quote, e, fmtlen);
272 membuf_added(&jout->outbuf, fmtlen + (int)quote*2);
279 bool json_out_add(struct json_out *jout,
280 const char *fieldname,
282 const char *fmt, ...)
288 ret = json_out_addv(jout, fieldname, quote, fmt, ap);
293 bool json_out_addstr(struct json_out *jout,
294 const char *fieldname,
297 size_t len = strlen(str);
299 struct json_escape *e;
301 if (json_escape_needed(str, len)) {
302 e = json_escape(NULL, str);
308 p = json_out_member_direct(jout, fieldname, len + 2);
310 p[0] = p[1+len] = '"';
311 memcpy(p+1, str, len);
318 bool json_out_add_splice(struct json_out *jout,
319 const char *fieldname,
320 const struct json_out *src)
325 p = json_out_contents(src, &len);
328 memcpy(json_out_member_direct(jout, fieldname, len), p, len);
332 char *json_out_direct(struct json_out *jout, size_t len)
334 char *p = mkroom(jout, len);
336 membuf_added(&jout->outbuf, len);
340 void json_out_finished(const struct json_out *jout)
342 #ifdef CCAN_JSON_OUT_DEBUG
343 assert(tal_count(jout->wrapping) == 0);
347 const char *json_out_contents(const struct json_out *jout, size_t *len)
349 *len = membuf_num_elems(&jout->outbuf);
350 return *len ? membuf_elems(&jout->outbuf) : NULL;
353 void json_out_consume(struct json_out *jout, size_t len)
355 membuf_consume(&jout->outbuf, len);