]> git.ozlabs.org Git - ccan/blobdiff - ccan/json_out/json_out.c
json_out: pass through OOM failures.
[ccan] / ccan / json_out / json_out.c
index b8c798fc0dae7527cbf5986da619cdf7cb8ed437..87deaca85f9ce0375ff9bd299ced923669f7c739 100644 (file)
@@ -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;
 }