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 {
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 */
......@@ -27,16 +27,23 @@ static void *membuf_tal_realloc(struct membuf *mb,
{
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
......@@ -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);
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,
......@@ -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)
{
#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;
}
......@@ -86,11 +103,14 @@ static void indent(struct json_out *jout, char type)
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;
}
......@@ -110,18 +130,22 @@ static void check_fieldname(const struct json_out *jout,
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
}
......@@ -139,12 +163,9 @@ char *json_out_member_direct(struct json_out *jout,
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++) = ',';
......@@ -162,21 +183,33 @@ out:
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,
......@@ -186,7 +219,8 @@ void json_out_addv(struct json_out *jout,
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);
......@@ -210,52 +244,78 @@ void json_out_addv(struct json_out *jout,
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)
{
......@@ -263,13 +323,17 @@ void json_out_add_splice(struct json_out *jout,
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;
}
......
......@@ -11,6 +11,8 @@ struct json_out;
/**
* 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);
......@@ -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.
* @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.
......@@ -72,15 +77,18 @@ void json_out_end(struct json_out *jout, char type);
* @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, ...);
......@@ -95,7 +103,7 @@ void json_out_add(struct json_out *jout,
*
* 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,
......@@ -109,7 +117,7 @@ void json_out_addv(struct json_out *jout,
*
* 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);
......@@ -120,7 +128,7 @@ void json_out_addstr(struct json_out *jout,
* @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.
*/
......@@ -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,
* 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
......@@ -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
* 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);
......
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