Commit 4fc7da62 authored by Rusty Russell's avatar Rusty Russell

json_out: pass through OOM failures.

And fix escaping to work with addstr(), and assert in debug mode
if they don't specify quotes.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 2fd44331
...@@ -11,7 +11,7 @@ struct json_out { ...@@ -11,7 +11,7 @@ struct json_out {
void *cb_arg; void *cb_arg;
#ifdef CCAN_JSON_OUT_DEBUG #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; char *wrapping;
#endif #endif
/* True if we haven't yet put an element in current wrapping */ /* True if we haven't yet put an element in current wrapping */
...@@ -27,16 +27,23 @@ static void *membuf_tal_realloc(struct membuf *mb, ...@@ -27,16 +27,23 @@ static void *membuf_tal_realloc(struct membuf *mb,
{ {
char *p = rawelems; char *p = rawelems;
tal_resize(&p, newsize); if (!tal_resize(&p, newsize))
return NULL;
return p; return p;
} }
struct json_out *json_out_new(const tal_t *ctx) struct json_out *json_out_new(const tal_t *ctx)
{ {
struct json_out *jout = tal(ctx, struct json_out); struct json_out *jout = tal(ctx, struct json_out);
char *pool;
membuf_init(&jout->outbuf, if (!jout)
tal_arr(jout, char, 64), 64, membuf_tal_realloc); 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 #ifdef CCAN_JSON_OUT_DEBUG
jout->wrapping = tal_arr(jout, char, 0); jout->wrapping = tal_arr(jout, char, 0);
#endif #endif
...@@ -61,9 +68,14 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src) ...@@ -61,9 +68,14 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
size_t num_elems = membuf_num_elems(&src->outbuf); size_t num_elems = membuf_num_elems(&src->outbuf);
char *elems = membuf_elems(&src->outbuf); char *elems = membuf_elems(&src->outbuf);
struct json_out *jout = tal_dup(ctx, struct json_out, src); struct json_out *jout = tal_dup(ctx, struct json_out, src);
membuf_init(&jout->outbuf, char *pool;
tal_dup_arr(jout, char, elems, num_elems, 0),
num_elems, membuf_tal_realloc); 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); membuf_added(&jout->outbuf, num_elems);
#ifdef CCAN_JSON_OUT_DEBUG #ifdef CCAN_JSON_OUT_DEBUG
jout->wrapping = tal_dup_arr(jout, char, jout->wrapping = tal_dup_arr(jout, char,
...@@ -76,9 +88,14 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src) ...@@ -76,9 +88,14 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src)
static void indent(struct json_out *jout, char type) static void indent(struct json_out *jout, char type)
{ {
#ifdef CCAN_JSON_OUT_DEBUG #ifdef CCAN_JSON_OUT_DEBUG
size_t n = tal_count(jout->wrapping); /* Can't check if we ran out of memory. */
tal_resize(&jout->wrapping, n+1); if (jout->wrapping) {
jout->wrapping[n] = type; 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 #endif
jout->empty = true; jout->empty = true;
} }
...@@ -86,11 +103,14 @@ static void indent(struct json_out *jout, char type) ...@@ -86,11 +103,14 @@ static void indent(struct json_out *jout, char type)
static void unindent(struct json_out *jout, char type) static void unindent(struct json_out *jout, char type)
{ {
#ifdef CCAN_JSON_OUT_DEBUG #ifdef CCAN_JSON_OUT_DEBUG
size_t indent = tal_count(jout->wrapping); /* Can't check if we ran out of memory. */
assert(indent > 0); if (jout->wrapping) {
/* Both [ and ] and { and } are two apart in ASCII */ size_t indent = tal_count(jout->wrapping);
assert(jout->wrapping[indent-1] == type - 2); assert(indent > 0);
tal_resize(&jout->wrapping, indent-1); /* Both [ and ] and { and } are two apart in ASCII */
assert(jout->wrapping[indent-1] == type - 2);
tal_resize(&jout->wrapping, indent-1);
}
#endif #endif
jout->empty = false; jout->empty = false;
} }
...@@ -110,18 +130,22 @@ static void check_fieldname(const struct json_out *jout, ...@@ -110,18 +130,22 @@ static void check_fieldname(const struct json_out *jout,
const char *fieldname) const char *fieldname)
{ {
#ifdef CCAN_JSON_OUT_DEBUG #ifdef CCAN_JSON_OUT_DEBUG
size_t n = tal_count(jout->wrapping); /* We don't escape this for you */
if (n == 0) assert(!fieldname || !json_escape_needed(fieldname, strlen(fieldname)));
/* Can't have a fieldname if not in anything! */
assert(!fieldname); /* Can't check anything else if we ran out of memory. */
else if (jout->wrapping[n-1] == '[') if (jout->wrapping) {
/* No fieldnames in arrays. */ size_t n = tal_count(jout->wrapping);
assert(!fieldname); if (n == 0)
else { /* Can't have a fieldname if not in anything! */
/* Must have fieldnames in objects. */ assert(!fieldname);
assert(fieldname); else if (jout->wrapping[n-1] == '[')
/* We don't escape this for you */ /* No fieldnames in arrays. */
assert(!json_escape_needed(fieldname, strlen(fieldname))); assert(!fieldname);
else {
/* Must have fieldnames in objects. */
assert(fieldname);
}
} }
#endif #endif
} }
...@@ -139,12 +163,9 @@ char *json_out_member_direct(struct json_out *jout, ...@@ -139,12 +163,9 @@ char *json_out_member_direct(struct json_out *jout,
if (fieldname) if (fieldname)
extra += 1 + strlen(fieldname) + 2; extra += 1 + strlen(fieldname) + 2;
if (!extra) {
dest = NULL;
goto out;
}
dest = mkroom(jout, extra); dest = mkroom(jout, extra);
if (!dest)
goto out;
if (!jout->empty) if (!jout->empty)
*(dest++) = ','; *(dest++) = ',';
...@@ -162,21 +183,33 @@ out: ...@@ -162,21 +183,33 @@ out:
return 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 == '{'); 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); 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 == ']'); assert(type == '}' || type == ']');
json_out_direct(jout, 1)[0] = type; p = json_out_direct(jout, 1);
if (p)
p[0] = type;
unindent(jout, 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, const char *fieldname,
bool quote, bool quote,
const char *fmt, const char *fmt,
...@@ -186,7 +219,8 @@ void json_out_addv(struct json_out *jout, ...@@ -186,7 +219,8 @@ void json_out_addv(struct json_out *jout,
va_list ap2; va_list ap2;
char *dst; 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. */ /* Make a copy in case we need it below. */
va_copy(ap2, ap); va_copy(ap2, ap);
...@@ -210,52 +244,78 @@ void json_out_addv(struct json_out *jout, ...@@ -210,52 +244,78 @@ void json_out_addv(struct json_out *jout,
if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) { if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) {
/* Make room for NUL terminator, even though we don't want it */ /* Make room for NUL terminator, even though we don't want it */
dst = mkroom(jout, fmtlen + 1 + (int)quote*2); dst = mkroom(jout, fmtlen + 1 + (int)quote*2);
if (!dst)
goto out;
vsprintf(dst + quote, fmt, ap2); vsprintf(dst + quote, fmt, ap2);
} }
/* Of course, if we need to escape it, we have to redo it all. */ #ifdef CCAN_JSON_OUT_DEBUG
if (json_escape_needed(dst + quote, fmtlen)) { /* You're not inserting junk here, are you? */
struct json_escape *e; assert(quote || !json_escape_needed(dst, fmtlen));
e = json_escape_len(NULL, dst + quote, fmtlen); #endif
fmtlen = strlen(e->s);
dst = mkroom(jout, fmtlen + (int)quote*2);
memcpy(dst + quote, e, fmtlen);
tal_free(e);
}
/* Of course, if we need to escape it, we have to redo it all. */
if (quote) { 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[0] = '"';
dst[fmtlen+1] = '"'; dst[fmtlen+1] = '"';
} }
membuf_added(&jout->outbuf, fmtlen + (int)quote*2); membuf_added(&jout->outbuf, fmtlen + (int)quote*2);
out:
va_end(ap2); 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, const char *fieldname,
bool quote, bool quote,
const char *fmt, ...) const char *fmt, ...)
{ {
va_list ap; va_list ap;
bool ret;
va_start(ap, fmt); va_start(ap, fmt);
json_out_addv(jout, fieldname, quote, fmt, ap); ret = json_out_addv(jout, fieldname, quote, fmt, ap);
va_end(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 *fieldname,
const char *str) const char *str)
{ {
size_t len = strlen(str); size_t len = strlen(str);
char *p; 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 = json_out_member_direct(jout, fieldname, len + 2);
p[0] = p[1+len] = '"'; if (p) {
memcpy(p+1, str, len); 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 char *fieldname,
const struct json_out *src) const struct json_out *src)
{ {
...@@ -263,13 +323,17 @@ void json_out_add_splice(struct json_out *jout, ...@@ -263,13 +323,17 @@ void json_out_add_splice(struct json_out *jout,
size_t len; size_t len;
p = json_out_contents(src, &len); p = json_out_contents(src, &len);
if (!p)
return false;
memcpy(json_out_member_direct(jout, fieldname, len), p, len); memcpy(json_out_member_direct(jout, fieldname, len), p, len);
return true;
} }
char *json_out_direct(struct json_out *jout, size_t len) char *json_out_direct(struct json_out *jout, size_t len)
{ {
char *p = mkroom(jout, len); char *p = mkroom(jout, len);
membuf_added(&jout->outbuf, len); if (p)
membuf_added(&jout->outbuf, len);
return p; return p;
} }
......
...@@ -11,6 +11,8 @@ struct json_out; ...@@ -11,6 +11,8 @@ struct json_out;
/** /**
* json_out_new - allocate a json_out stream. * json_out_new - allocate a json_out stream.
* @ctx: the tal_context to allocate from, or NULL * @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); struct json_out *json_out_new(const tal_t *ctx);
...@@ -50,20 +52,23 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src); ...@@ -50,20 +52,23 @@ struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src);
* @fieldname: the fieldname, if inside an object, or NULL if inside an array. * @fieldname: the fieldname, if inside an object, or NULL if inside an array.
* @type: '[' or '{' to start an array or object, respectively. * @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. * Literally writes '"@fieldname": @type' or '@type ' if fieldname is NULL.
* @fieldname must not need JSON escaping. * @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. * json_out_end - end an array or object.
* @jout: the json_out object to write into. * @jout: the json_out object to write into.
* @type: '}' or ']' to end an array or object, respectively. * @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 * Literally writes ']' or '}', keeping track of whether we need to append
* a comma. * 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. * json_out_add - add a formatted member.
...@@ -72,15 +77,18 @@ void json_out_end(struct json_out *jout, char type); ...@@ -72,15 +77,18 @@ void json_out_end(struct json_out *jout, char type);
* @quote: if true, surround fmt by " and ". * @quote: if true, surround fmt by " and ".
* @fmt...: the printf-style format * @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 * 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 * object, @fieldname must be non-NULL. This is checked if
* CCAN_JSON_OUT_DEBUG is defined. * CCAN_JSON_OUT_DEBUG is defined.
* @fieldname must not need JSON escaping. * @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) PRINTF_FMT(4,5)
void json_out_add(struct json_out *jout, bool json_out_add(struct json_out *jout,
const char *fieldname, const char *fieldname,
bool quote, bool quote,
const char *fmt, ...); const char *fmt, ...);
...@@ -95,7 +103,7 @@ void json_out_add(struct json_out *jout, ...@@ -95,7 +103,7 @@ void json_out_add(struct json_out *jout,
* *
* See json_out_add() above. * See json_out_add() above.
*/ */
void json_out_addv(struct json_out *jout, bool json_out_addv(struct json_out *jout,
const char *fieldname, const char *fieldname,
bool quote, bool quote,
const char *fmt, const char *fmt,
...@@ -109,7 +117,7 @@ void json_out_addv(struct json_out *jout, ...@@ -109,7 +117,7 @@ void json_out_addv(struct json_out *jout,
* *
* Equivalent to json_out_add(@jout, @fieldname, true, "%s", @str); * 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 *fieldname,
const char *str); const char *str);
...@@ -120,7 +128,7 @@ void json_out_addstr(struct json_out *jout, ...@@ -120,7 +128,7 @@ void json_out_addstr(struct json_out *jout,
* @extra: how many bytes to allocate. * @extra: how many bytes to allocate.
* *
* @fieldname must not need JSON escaping. Returns a direct pointer into * @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 allows you to write your own efficient type-specific helpers.
*/ */
...@@ -135,7 +143,8 @@ char *json_out_member_direct(struct json_out *jout, ...@@ -135,7 +143,8 @@ char *json_out_member_direct(struct json_out *jout,
* This lets you access the json_out stream directly, to save a copy, * This lets you access the json_out stream directly, to save a copy,
* if you know exactly how much you will write. * 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 "," * This is dangerous, since it doesn't automatically prepend a ","
* like the internal logic does, but can be used (carefully) to add * like the internal logic does, but can be used (carefully) to add
...@@ -156,8 +165,10 @@ char *json_out_direct(struct json_out *jout, size_t extra); ...@@ -156,8 +165,10 @@ char *json_out_direct(struct json_out *jout, size_t extra);
* *
* Note that it will call json_out_contents(@src), so it expects that * Note that it will call json_out_contents(@src), so it expects that
* object to be unconsumed. * 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 char *fieldname,
const struct json_out *src); const struct json_out *src);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment