void *cb_arg;
#ifdef CCAN_JSON_OUT_DEBUG
- /* tal_arr of types ( or [ we're enclosed in. */
+ /* tal_arr of types ( or [ we're enclosed in. NULL if oom. */
char *wrapping;
#endif
/* True if we haven't yet put an element in current wrapping */
{
char *p = rawelems;
- tal_resize(&p, newsize);
+ if (!tal_resize(&p, newsize))
+ return NULL;
return p;
}
struct json_out *json_out_new(const tal_t *ctx)
{
struct json_out *jout = tal(ctx, struct json_out);
+ char *pool;
- membuf_init(&jout->outbuf,
- tal_arr(jout, char, 64), 64, membuf_tal_realloc);
+ if (!jout)
+ return NULL;
+ pool = tal_arr(jout, char, 64);
+ if (!pool)
+ return tal_free(jout);
+
+ membuf_init(&jout->outbuf, pool, tal_count(pool), membuf_tal_realloc);
#ifdef CCAN_JSON_OUT_DEBUG
jout->wrapping = tal_arr(jout, char, 0);
#endif
size_t num_elems = membuf_num_elems(&src->outbuf);
char *elems = membuf_elems(&src->outbuf);
struct json_out *jout = tal_dup(ctx, struct json_out, src);
- membuf_init(&jout->outbuf,
- tal_dup_arr(jout, char, elems, num_elems, 0),
- num_elems, membuf_tal_realloc);
+ char *pool;
+
+ if (!jout)
+ return NULL;
+ pool = tal_dup_arr(jout, char, elems, num_elems, 0);
+ if (!pool)
+ return tal_free(jout);
+ membuf_init(&jout->outbuf, pool, num_elems, membuf_tal_realloc);
membuf_added(&jout->outbuf, num_elems);
#ifdef CCAN_JSON_OUT_DEBUG
jout->wrapping = tal_dup_arr(jout, char,
static void indent(struct json_out *jout, char type)
{
#ifdef CCAN_JSON_OUT_DEBUG
- size_t n = tal_count(jout->wrapping);
- tal_resize(&jout->wrapping, n+1);
- jout->wrapping[n] = type;
+ /* Can't check if we ran out of memory. */
+ if (jout->wrapping) {
+ size_t n = tal_count(jout->wrapping);
+ if (!tal_resize(&jout->wrapping, n+1))
+ jout->wrapping = tal_free(jout->wrapping);
+ else
+ jout->wrapping[n] = type;
+ }
#endif
jout->empty = true;
}
static void unindent(struct json_out *jout, char type)
{
#ifdef CCAN_JSON_OUT_DEBUG
- size_t indent = tal_count(jout->wrapping);
- assert(indent > 0);
- /* Both [ and ] and { and } are two apart in ASCII */
- assert(jout->wrapping[indent-1] == type - 2);
- tal_resize(&jout->wrapping, indent-1);
+ /* Can't check if we ran out of memory. */
+ if (jout->wrapping) {
+ size_t indent = tal_count(jout->wrapping);
+ assert(indent > 0);
+ /* Both [ and ] and { and } are two apart in ASCII */
+ assert(jout->wrapping[indent-1] == type - 2);
+ tal_resize(&jout->wrapping, indent-1);
+ }
#endif
jout->empty = false;
}
const char *fieldname)
{
#ifdef CCAN_JSON_OUT_DEBUG
- size_t n = tal_count(jout->wrapping);
- if (n == 0)
- /* Can't have a fieldname if not in anything! */
- assert(!fieldname);
- else if (jout->wrapping[n-1] == '[')
- /* No fieldnames in arrays. */
- assert(!fieldname);
- else {
- /* Must have fieldnames in objects. */
- assert(fieldname);
- /* We don't escape this for you */
- assert(!json_escape_needed(fieldname, strlen(fieldname)));
+ /* We don't escape this for you */
+ assert(!fieldname || !json_escape_needed(fieldname, strlen(fieldname)));
+
+ /* Can't check anything else if we ran out of memory. */
+ if (jout->wrapping) {
+ size_t n = tal_count(jout->wrapping);
+ if (n == 0)
+ /* Can't have a fieldname if not in anything! */
+ assert(!fieldname);
+ else if (jout->wrapping[n-1] == '[')
+ /* No fieldnames in arrays. */
+ assert(!fieldname);
+ else {
+ /* Must have fieldnames in objects. */
+ assert(fieldname);
+ }
}
#endif
}
if (fieldname)
extra += 1 + strlen(fieldname) + 2;
- if (!extra) {
- dest = NULL;
- goto out;
- }
-
dest = mkroom(jout, extra);
+ if (!dest)
+ goto out;
if (!jout->empty)
*(dest++) = ',';
return dest;
}
-void json_out_start(struct json_out *jout, const char *fieldname, char type)
+bool json_out_start(struct json_out *jout, const char *fieldname, char type)
{
+ char *p;
+
assert(type == '[' || type == '{');
- json_out_member_direct(jout, fieldname, 1)[0] = type;
+ p = json_out_member_direct(jout, fieldname, 1);
+ if (p)
+ p[0] = type;
indent(jout, type);
+
+ return p != NULL;
}
-void json_out_end(struct json_out *jout, char type)
+bool json_out_end(struct json_out *jout, char type)
{
+ char *p;
+
assert(type == '}' || type == ']');
- json_out_direct(jout, 1)[0] = type;
+ p = json_out_direct(jout, 1);
+ if (p)
+ p[0] = type;
unindent(jout, type);
+
+ return p != NULL;
}
-void json_out_addv(struct json_out *jout,
+bool json_out_addv(struct json_out *jout,
const char *fieldname,
bool quote,
const char *fmt,
va_list ap2;
char *dst;
- json_out_member_direct(jout, fieldname, 0);
+ if (!json_out_member_direct(jout, fieldname, 0))
+ return false;
/* Make a copy in case we need it below. */
va_copy(ap2, ap);
if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) {
/* Make room for NUL terminator, even though we don't want it */
dst = mkroom(jout, fmtlen + 1 + (int)quote*2);
+ if (!dst)
+ goto out;
vsprintf(dst + quote, fmt, ap2);
}
- /* Of course, if we need to escape it, we have to redo it all. */
- if (json_escape_needed(dst + quote, fmtlen)) {
- struct json_escape *e;
- e = json_escape_len(NULL, dst + quote, fmtlen);
- fmtlen = strlen(e->s);
- dst = mkroom(jout, fmtlen + (int)quote*2);
- memcpy(dst + quote, e, fmtlen);
- tal_free(e);
- }
+#ifdef CCAN_JSON_OUT_DEBUG
+ /* You're not inserting junk here, are you? */
+ assert(quote || !json_escape_needed(dst, fmtlen));
+#endif
+ /* Of course, if we need to escape it, we have to redo it all. */
if (quote) {
+ if (json_escape_needed(dst + quote, fmtlen)) {
+ struct json_escape *e;
+ e = json_escape_len(NULL, dst + quote, fmtlen);
+ fmtlen = strlen(e->s);
+ dst = mkroom(jout, fmtlen + (int)quote*2);
+ if (!dst)
+ goto out;
+ memcpy(dst + quote, e, fmtlen);
+ tal_free(e);
+ }
dst[0] = '"';
dst[fmtlen+1] = '"';
}
membuf_added(&jout->outbuf, fmtlen + (int)quote*2);
+
+out:
va_end(ap2);
+ return dst != NULL;
}
-void json_out_add(struct json_out *jout,
+bool json_out_add(struct json_out *jout,
const char *fieldname,
bool quote,
const char *fmt, ...)
{
va_list ap;
+ bool ret;
va_start(ap, fmt);
- json_out_addv(jout, fieldname, quote, fmt, ap);
+ ret = json_out_addv(jout, fieldname, quote, fmt, ap);
va_end(ap);
+ return ret;
}
-void json_out_addstr(struct json_out *jout,
+bool json_out_addstr(struct json_out *jout,
const char *fieldname,
const char *str)
{
size_t len = strlen(str);
char *p;
+ struct json_escape *e;
+
+ if (json_escape_needed(str, len)) {
+ e = json_escape(NULL, str);
+ str = e->s;
+ len = strlen(str);
+ } else
+ e = NULL;
p = json_out_member_direct(jout, fieldname, len + 2);
- p[0] = p[1+len] = '"';
- memcpy(p+1, str, len);
+ if (p) {
+ p[0] = p[1+len] = '"';
+ memcpy(p+1, str, len);
+ }
+ tal_free(e);
+
+ return p != NULL;
}
-void json_out_add_splice(struct json_out *jout,
+bool json_out_add_splice(struct json_out *jout,
const char *fieldname,
const struct json_out *src)
{
size_t len;
p = json_out_contents(src, &len);
+ if (!p)
+ return false;
memcpy(json_out_member_direct(jout, fieldname, len), p, len);
+ return true;
}
char *json_out_direct(struct json_out *jout, size_t len)
{
char *p = mkroom(jout, len);
- membuf_added(&jout->outbuf, len);
+ if (p)
+ membuf_added(&jout->outbuf, len);
return p;
}
/**
* json_out_new - allocate a json_out stream.
* @ctx: the tal_context to allocate from, or NULL
+ *
+ * Returns NULL if tal allocation fails.
*/
struct json_out *json_out_new(const tal_t *ctx);
* @fieldname: the fieldname, if inside an object, or NULL if inside an array.
* @type: '[' or '{' to start an array or object, respectively.
*
+ * Returns true unless tal_resize() fails.
* Literally writes '"@fieldname": @type' or '@type ' if fieldname is NULL.
* @fieldname must not need JSON escaping.
*/
-void json_out_start(struct json_out *jout, const char *fieldname, char type);
+bool json_out_start(struct json_out *jout, const char *fieldname, char type);
/**
* json_out_end - end an array or object.
* @jout: the json_out object to write into.
* @type: '}' or ']' to end an array or object, respectively.
*
+ * Returns true unless tal_resize() fails.
+ *
* Literally writes ']' or '}', keeping track of whether we need to append
* a comma.
*/
-void json_out_end(struct json_out *jout, char type);
+bool json_out_end(struct json_out *jout, char type);
/**
* json_out_add - add a formatted member.
* @quote: if true, surround fmt by " and ".
* @fmt...: the printf-style format
*
+ * Returns true unless tal_resize() fails.
+ *
* If you're in an array, @fieldname must be NULL. If you're in an
* object, @fieldname must be non-NULL. This is checked if
* CCAN_JSON_OUT_DEBUG is defined.
* @fieldname must not need JSON escaping.
*
- * If the resulting string requires escaping, we call json_escape().
+ * If the resulting string requires escaping, and @quote is true, we
+ * call json_escape().
*/
PRINTF_FMT(4,5)
-void json_out_add(struct json_out *jout,
+bool json_out_add(struct json_out *jout,
const char *fieldname,
bool quote,
const char *fmt, ...);
*
* See json_out_add() above.
*/
-void json_out_addv(struct json_out *jout,
+bool json_out_addv(struct json_out *jout,
const char *fieldname,
bool quote,
const char *fmt,
*
* Equivalent to json_out_add(@jout, @fieldname, true, "%s", @str);
*/
-void json_out_addstr(struct json_out *jout,
+bool json_out_addstr(struct json_out *jout,
const char *fieldname,
const char *str);
* @extra: how many bytes to allocate.
*
* @fieldname must not need JSON escaping. Returns a direct pointer into
- * the @extra bytes.
+ * the @extra bytes, or NULL if tal_resize() fails.
*
* This allows you to write your own efficient type-specific helpers.
*/
* This lets you access the json_out stream directly, to save a copy,
* if you know exactly how much you will write.
*
- * Returns a pointer to @len bytes at the end of @jout.
+ * Returns a pointer to @len bytes at the end of @jout, or NULL if
+ * tal_resize() fails.
*
* This is dangerous, since it doesn't automatically prepend a ","
* like the internal logic does, but can be used (carefully) to add
*
* Note that it will call json_out_contents(@src), so it expects that
* object to be unconsumed.
+ *
+ * Returns false if tal_resize() fails.
*/
-void json_out_add_splice(struct json_out *jout,
+bool json_out_add_splice(struct json_out *jout,
const char *fieldname,
const struct json_out *src);