netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / lib / json.c
index cdcfba5..dd85213 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nicira Networks.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 #include "json.h"
 
-#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>
 #include <limits.h>
-#include <math.h>
 #include <string.h>
 
 #include "dynamic-string.h"
@@ -99,6 +97,9 @@ struct json_parser {
     /* Lexical analysis. */
     enum json_lex_state lex_state;
     struct ds buffer;           /* Buffer for accumulating token text. */
+    int line_number;
+    int column_number;
+    int byte_number;
 
     /* Parsing. */
     enum json_parse_state parse_state;
@@ -116,7 +117,7 @@ static struct json *json_create(enum json_type type);
 static void json_parser_input(struct json_parser *, struct json_token *);
 
 static void json_error(struct json_parser *p, const char *format, ...)
-    PRINTF_FORMAT(2, 3);
+    OVS_PRINTF_FORMAT(2, 3);
 \f
 const char *
 json_type_to_string(enum json_type type)
@@ -219,6 +220,14 @@ json_array_create(struct json **elements, size_t n)
     return json;
 }
 
+struct json *
+json_array_create_1(struct json *elem0)
+{
+    struct json **elems = xmalloc(sizeof *elems);
+    elems[0] = elem0;
+    return json_array_create(elems, 1);
+}
+
 struct json *
 json_array_create_2(struct json *elem0, struct json *elem1)
 {
@@ -266,7 +275,7 @@ json_real_create(double real)
 void
 json_object_put(struct json *json, const char *name, struct json *value)
 {
-    shash_add(json->u.object, name, value);
+    json_destroy(shash_replace(json->u.object, name, value));
 }
 
 void
@@ -278,42 +287,42 @@ json_object_put_string(struct json *json, const char *name, const char *value)
 const char *
 json_string(const struct json *json)
 {
-    assert(json->type == JSON_STRING);
+    ovs_assert(json->type == JSON_STRING);
     return json->u.string;
 }
 
 struct json_array *
 json_array(const struct json *json)
 {
-    assert(json->type == JSON_ARRAY);
-    return (struct json_array *) &json->u.array;
+    ovs_assert(json->type == JSON_ARRAY);
+    return CONST_CAST(struct json_array *, &json->u.array);
 }
 
 struct shash *
 json_object(const struct json *json)
 {
-    assert(json->type == JSON_OBJECT);
-    return (struct shash *) json->u.object;
+    ovs_assert(json->type == JSON_OBJECT);
+    return CONST_CAST(struct shash *, json->u.object);
 }
 
 bool
 json_boolean(const struct json *json)
 {
-    assert(json->type == JSON_TRUE || json->type == JSON_FALSE);
+    ovs_assert(json->type == JSON_TRUE || json->type == JSON_FALSE);
     return json->type == JSON_TRUE;
 }
 
 double
 json_real(const struct json *json)
 {
-    assert(json->type == JSON_REAL || json->type == JSON_INTEGER);
+    ovs_assert(json->type == JSON_REAL || json->type == JSON_INTEGER);
     return json->type == JSON_REAL ? json->u.real : json->u.integer;
 }
 
 int64_t
 json_integer(const struct json *json)
 {
-    assert(json->type == JSON_INTEGER);
+    ovs_assert(json->type == JSON_INTEGER);
     return json->u.integer;
 }
 \f
@@ -346,7 +355,7 @@ json_destroy(struct json *json)
             break;
 
         case JSON_N_TYPES:
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
         free(json);
     }
@@ -408,7 +417,7 @@ json_clone(const struct json *json)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -452,6 +461,7 @@ json_hash_object(const struct shash *object, size_t basis)
         basis = hash_string(node->name, basis);
         basis = json_hash(node->data, basis);
     }
+    free(nodes);
     return basis;
 }
 
@@ -493,7 +503,7 @@ json_hash(const struct json *json, size_t basis)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -564,7 +574,7 @@ json_equal(const struct json *a, const struct json *b)
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 \f
@@ -595,7 +605,7 @@ json_lex_number(struct json_parser *p)
 {
     const char *cp = ds_cstr(&p->buffer);
     unsigned long long int significand = 0;
-    int sig_digits = 0;
+    struct json_token token;
     bool imprecise = false;
     bool negative = false;
     int pow10 = 0;
@@ -609,18 +619,16 @@ json_lex_number(struct json_parser *p)
     /* At least one integer digit, but 0 may not be used as a leading digit for
      * a longer number. */
     significand = 0;
-    sig_digits = 0;
     if (*cp == '0') {
         cp++;
-        if (isdigit(*cp)) {
+        if (isdigit((unsigned char) *cp)) {
             json_error(p, "leading zeros not allowed");
             return;
         }
-    } else if (isdigit(*cp)) {
+    } else if (isdigit((unsigned char) *cp)) {
         do {
             if (significand <= ULLONG_MAX / 10) {
                 significand = significand * 10 + (*cp - '0');
-                sig_digits++;
             } else {
                 pow10++;
                 if (*cp != '0') {
@@ -628,7 +636,7 @@ json_lex_number(struct json_parser *p)
                 }
             }
             cp++;
-        } while (isdigit(*cp));
+        } while (isdigit((unsigned char) *cp));
     } else {
         json_error(p, "'-' must be followed by digit");
         return;
@@ -637,20 +645,19 @@ json_lex_number(struct json_parser *p)
     /* Optional fraction. */
     if (*cp == '.') {
         cp++;
-        if (!isdigit(*cp)) {
+        if (!isdigit((unsigned char) *cp)) {
             json_error(p, "decimal point must be followed by digit");
             return;
         }
         do {
             if (significand <= ULLONG_MAX / 10) {
                 significand = significand * 10 + (*cp - '0');
-                sig_digits++;
                 pow10--;
             } else if (*cp != '0') {
                 imprecise = true;
             }
             cp++;
-        } while (isdigit(*cp));
+        } while (isdigit((unsigned char) *cp));
     }
 
     /* Optional exponent. */
@@ -666,7 +673,7 @@ json_lex_number(struct json_parser *p)
             cp++;
         }
 
-        if (!isdigit(*cp)) {
+        if (!isdigit((unsigned char) *cp)) {
             json_error(p, "exponent must contain at least one digit");
             return;
         }
@@ -679,7 +686,7 @@ json_lex_number(struct json_parser *p)
             }
             exponent = exponent * 10 + (*cp - '0');
             cp++;
-        } while (isdigit(*cp));
+        } while (isdigit((unsigned char) *cp));
 
         if (negative_exponent) {
             pow10 -= exponent;
@@ -697,7 +704,6 @@ json_lex_number(struct json_parser *p)
      *
      * We suppress negative zeros as a matter of policy. */
     if (!significand) {
-        struct json_token token;
         token.type = T_INTEGER;
         token.u.integer = 0;
         json_parser_input(p, &token);
@@ -707,19 +713,16 @@ json_lex_number(struct json_parser *p)
     if (!imprecise) {
         while (pow10 > 0 && significand < ULLONG_MAX / 10) {
             significand *= 10;
-            sig_digits++;
             pow10--;
         }
         while (pow10 < 0 && significand % 10 == 0) {
             significand /= 10;
-            sig_digits--;
             pow10++;
         }
         if (pow10 == 0
             && significand <= (negative
                                ? (unsigned long long int) LLONG_MAX + 1
                                : LLONG_MAX)) {
-            struct json_token token;
             token.type = T_INTEGER;
             token.u.integer = negative ? -significand : significand;
             json_parser_input(p, &token);
@@ -727,149 +730,197 @@ json_lex_number(struct json_parser *p)
         }
     }
 
-    if (pow10 + sig_digits <= DBL_MAX_10_EXP) {
-        struct json_token token;
-        token.type = T_REAL;
-        token.u.real = significand * pow(10.0, pow10);
-        if (token.u.real <= DBL_MAX) {
-            if (negative && token.u.real) {
-                token.u.real = -token.u.real;
-            }
-            json_parser_input(p, &token);
-            return;
-        }
+    token.type = T_REAL;
+    if (!str_to_double(ds_cstr(&p->buffer), &token.u.real)) {
+        json_error(p, "number outside valid range");
+        return;
     }
-    json_error(p, "number outside valid range");
+    /* Suppress negative zero. */
+    if (token.u.real == 0) {
+        token.u.real = 0;
+    }
+    json_parser_input(p, &token);
 }
 
-static bool
-json_lex_4hex(struct json_parser *p, const char *cp, int *valuep)
+static const char *
+json_lex_4hex(const char *cp, const char *end, int *valuep)
 {
-    int value, i;
+    unsigned int value;
+    bool ok;
 
-    value = 0;
-    for (i = 0; i < 4; i++) {
-        unsigned char c = *cp++;
-        if (!isxdigit(c)) {
-            json_error(p, "malformed \\u escape");
-            return false;
-        }
-        value = (value << 4) | hexit_value(c);
+    if (cp + 4 > end) {
+        return "quoted string ends within \\u escape";
+    }
+
+    value = hexits_value(cp, 4, &ok);
+    if (!ok) {
+        return "malformed \\u escape";
     }
     if (!value) {
-        json_error(p, "null bytes not supported in quoted strings");
-        return false;
+        return "null bytes not supported in quoted strings";
     }
     *valuep = value;
-    return true;
+    return NULL;
 }
 
 static const char *
-json_lex_unicode(struct json_parser *p, const char *cp, struct ds *s)
+json_lex_unicode(const char *cp, const char *end, struct ds *out)
 {
+    const char *error;
     int c0, c1;
 
-    if (!json_lex_4hex(p, cp, &c0)) {
+    error = json_lex_4hex(cp, end, &c0);
+    if (error) {
+        ds_clear(out);
+        ds_put_cstr(out, error);
         return NULL;
     }
     cp += 4;
     if (!uc_is_leading_surrogate(c0)) {
-        ds_put_utf8(s, c0);
+        ds_put_utf8(out, c0);
         return cp;
     }
 
-    if (*cp++ != '\\' || *cp++ != 'u') {
-        json_error(p, "malformed escaped surrogate pair");
+    if (cp + 2 > end || *cp++ != '\\' || *cp++ != 'u') {
+        ds_clear(out);
+        ds_put_cstr(out, "malformed escaped surrogate pair");
         return NULL;
     }
 
-    if (!json_lex_4hex(p, cp, &c1)) {
+    error = json_lex_4hex(cp, end, &c1);
+    if (error) {
+        ds_clear(out);
+        ds_put_cstr(out, error);
         return NULL;
     }
     cp += 4;
     if (!uc_is_trailing_surrogate(c1)) {
-        json_error(p, "second half of escaped surrogate pair is not "
-                   "trailing surrogate");
+        ds_clear(out);
+        ds_put_cstr(out, "second half of escaped surrogate pair is not "
+                    "trailing surrogate");
         return NULL;
     }
 
-    ds_put_utf8(s, utf16_decode_surrogate_pair(c0, c1));
+    ds_put_utf8(out, utf16_decode_surrogate_pair(c0, c1));
     return cp;
 }
 
-static void
-json_lex_string(struct json_parser *p)
-{
-    struct json_token token;
-    const char *cp;
-    struct ds s;
-
-    cp = ds_cstr(&p->buffer);
-    if (!strchr(cp, '\\')) {
-        token.type = T_STRING;
-        token.u.string = cp;
-        json_parser_input(p, &token);
-        return;
-    }
-
-    ds_init(&s);
-    ds_reserve(&s, strlen(cp));
-    while (*cp != '\0') {
-        if (*cp != '\\') {
-            ds_put_char(&s, *cp++);
+bool
+json_string_unescape(const char *in, size_t in_len, char **outp)
+{
+    const char *end = in + in_len;
+    bool ok = false;
+    struct ds out;
+
+    ds_init(&out);
+    ds_reserve(&out, in_len);
+    while (in < end) {
+        if (*in == '"') {
+            ds_clear(&out);
+            ds_put_cstr(&out, "quoted string may not include unescaped \"");
+            goto exit;
+        }
+        if (*in != '\\') {
+            ds_put_char(&out, *in++);
             continue;
         }
 
-        cp++;
-        switch (*cp++) {
+        in++;
+        if (in >= end) {
+            /* The JSON parser will never trigger this message, because its
+             * lexer will never pass in a string that ends in a single
+             * backslash, but json_string_unescape() has other callers that
+             * are not as careful.*/
+            ds_clear(&out);
+            ds_put_cstr(&out, "quoted string may not end with backslash");
+            goto exit;
+        }
+        switch (*in++) {
         case '"': case '\\': case '/':
-            ds_put_char(&s, cp[-1]);
+            ds_put_char(&out, in[-1]);
             break;
 
         case 'b':
-            ds_put_char(&s, '\b');
+            ds_put_char(&out, '\b');
             break;
 
         case 'f':
-            ds_put_char(&s, '\f');
+            ds_put_char(&out, '\f');
             break;
 
         case 'n':
-            ds_put_char(&s, '\n');
+            ds_put_char(&out, '\n');
             break;
 
         case 'r':
-            ds_put_char(&s, '\r');
+            ds_put_char(&out, '\r');
             break;
 
         case 't':
-            ds_put_char(&s, '\t');
+            ds_put_char(&out, '\t');
             break;
 
         case 'u':
-            cp = json_lex_unicode(p, cp, &s);
-            if (!cp) {
+            in = json_lex_unicode(in, end, &out);
+            if (!in) {
                 goto exit;
             }
             break;
 
         default:
-            json_error(p, "bad escape \\%c", cp[-1]);
+            ds_clear(&out);
+            ds_put_format(&out, "bad escape \\%c", in[-1]);
             goto exit;
         }
     }
+    ok = true;
+
+exit:
+    *outp = ds_cstr(&out);
+    return ok;
+}
+
+void
+json_string_escape(const char *in, struct ds *out)
+{
+    struct json json = {
+        .type = JSON_STRING,
+        .u.string = CONST_CAST(char *, in),
+    };
+    json_to_ds(&json, 0, out);
+}
+
+static void
+json_parser_input_string(struct json_parser *p, const char *s)
+{
+    struct json_token token;
 
     token.type = T_STRING;
-    token.u.string = ds_cstr(&s);
+    token.u.string = s;
     json_parser_input(p, &token);
+}
 
-exit:
-    ds_destroy(&s);
-    return;
+static void
+json_lex_string(struct json_parser *p)
+{
+    const char *raw = ds_cstr(&p->buffer);
+    if (!strchr(raw, '\\')) {
+        json_parser_input_string(p, raw);
+    } else {
+        char *cooked;
+
+        if (json_string_unescape(raw, strlen(raw), &cooked)) {
+            json_parser_input_string(p, cooked);
+        } else {
+            json_error(p, "%s", cooked);
+        }
+
+        free(cooked);
+    }
 }
 
 static bool
-json_lex_input(struct json_parser *p, int c)
+json_lex_input(struct json_parser *p, unsigned char c)
 {
     struct json_token token;
 
@@ -986,18 +1037,35 @@ json_from_string(const char *string)
 struct json *
 json_from_file(const char *file_name)
 {
-    struct json_parser *p;
     struct json *json;
     FILE *stream;
 
-    /* Open file. */
     stream = fopen(file_name, "r");
     if (!stream) {
         return json_string_create_nocopy(
-            xasprintf("error opening \"%s\": %s", file_name, strerror(errno)));
+            xasprintf("error opening \"%s\": %s", file_name,
+                      ovs_strerror(errno)));
     }
+    json = json_from_stream(stream);
+    fclose(stream);
+
+    return json;
+}
+
+/* Parses the contents of 'stream' as a JSON object or array, and returns a
+ * newly allocated 'struct json'.  The caller must free the returned structure
+ * with json_destroy() when it is no longer needed.
+ *
+ * The file must be encoded in UTF-8.
+ *
+ * See json_from_string() for return value semantics.
+ */
+struct json *
+json_from_stream(FILE *stream)
+{
+    struct json_parser *p;
+    struct json *json;
 
-    /* Read and parse file. */
     p = json_parser_create(JSPF_TRAILER);
     for (;;) {
         char buffer[BUFSIZ];
@@ -1010,13 +1078,11 @@ json_from_file(const char *file_name)
     }
     json = json_parser_finish(p);
 
-    /* Close file and check for I/O errors. */
     if (ferror(stream)) {
         json_destroy(json);
         json = json_string_create_nocopy(
-            xasprintf("error reading \"%s\": %s", file_name, strerror(errno)));
+            xasprintf("error reading JSON stream: %s", ovs_strerror(errno)));
     }
-    fclose(stream);
 
     return json;
 }
@@ -1035,6 +1101,13 @@ json_parser_feed(struct json_parser *p, const char *input, size_t n)
     size_t i;
     for (i = 0; !p->done && i < n; ) {
         if (json_lex_input(p, input[i])) {
+            p->byte_number++;
+            if (input[i] == '\n') {
+                p->column_number = 0;
+                p->line_number++;
+            } else {
+                p->column_number++;
+            }
             i++;
         }
     }
@@ -1074,8 +1147,8 @@ json_parser_finish(struct json_parser *p)
     }
 
     if (!p->error) {
-        assert(p->height == 1);
-        assert(p->stack[0].json != NULL);
+        ovs_assert(p->height == 1);
+        ovs_assert(p->stack[0].json != NULL);
         json = p->stack[--p->height].json;
     } else {
         json = json_string_create_nocopy(p->error);
@@ -1119,11 +1192,11 @@ json_parser_put_value(struct json_parser *p, struct json *value)
     } else if (node->json->type == JSON_ARRAY) {
         json_array_add(node->json, value);
     } else {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
-static struct json_parser_node *
+static void
 json_parser_push(struct json_parser *p,
                  struct json *new_json, enum json_parse_state new_state)
 {
@@ -1142,11 +1215,10 @@ json_parser_push(struct json_parser *p,
         node = &p->stack[p->height++];
         node->json = new_json;
         p->parse_state = new_state;
-        return node;
     } else {
+        json_destroy(new_json);
         json_error(p, "input exceeds maximum nesting depth %d",
                    JSON_MAX_HEIGHT);
-        return NULL;
     }
 }
 
@@ -1240,7 +1312,7 @@ json_parser_pop(struct json_parser *p)
         } else if (node->json->type == JSON_OBJECT) {
             p->parse_state = JSON_PARSE_OBJECT_NEXT;
         } else {
-            NOT_REACHED();
+            OVS_NOT_REACHED();
         }
     }
 }
@@ -1340,12 +1412,18 @@ static void
 json_error(struct json_parser *p, const char *format, ...)
 {
     if (!p->error) {
+        struct ds msg;
         va_list args;
 
+        ds_init(&msg);
+        ds_put_format(&msg, "line %d, column %d, byte %d: ",
+                      p->line_number, p->column_number, p->byte_number);
         va_start(args, format);
-        p->error = xvasprintf(format, args);
+        ds_put_format_valist(&msg, format, args);
         va_end(args);
 
+        p->error = ds_steal_cstr(&msg);
+
         p->done = true;
     }
 }
@@ -1353,17 +1431,17 @@ json_error(struct json_parser *p, const char *format, ...)
 #define SPACES_PER_LEVEL 2
 
 struct json_serializer {
-    struct ds ds;
+    struct ds *ds;
     int depth;
     int flags;
 };
 
-static void json_to_ds(const struct json *, struct json_serializer *);
-static void json_object_to_ds(const struct shash *object,
-                              struct json_serializer *);
-static void json_array_to_ds(const struct json_array *,
-                             struct json_serializer *);
-static void json_string_to_ds(const char *string, struct ds *);
+static void json_serialize(const struct json *, struct json_serializer *);
+static void json_serialize_object(const struct shash *object,
+                                  struct json_serializer *);
+static void json_serialize_array(const struct json_array *,
+                                 struct json_serializer *);
+static void json_serialize_string(const char *, struct ds *);
 
 /* Converts 'json' to a string in JSON format, encoded in UTF-8, and returns
  * that string.  The caller is responsible for freeing the returned string,
@@ -1381,19 +1459,30 @@ static void json_string_to_ds(const char *string, struct ds *);
  * object, since a bare literal does not satisfy the JSON grammar. */
 char *
 json_to_string(const struct json *json, int flags)
+{
+    struct ds ds;
+
+    ds_init(&ds);
+    json_to_ds(json, flags, &ds);
+    return ds_steal_cstr(&ds);
+}
+
+/* Same as json_to_string(), but the output is appended to 'ds'. */
+void
+json_to_ds(const struct json *json, int flags, struct ds *ds)
 {
     struct json_serializer s;
-    ds_init(&s.ds);
+
+    s.ds = ds;
     s.depth = 0;
     s.flags = flags;
-    json_to_ds(json, &s);
-    return ds_steal_cstr(&s.ds);
+    json_serialize(json, &s);
 }
 
 static void
-json_to_ds(const struct json *json, struct json_serializer *s)
+json_serialize(const struct json *json, struct json_serializer *s)
 {
-    struct ds *ds = &s->ds;
+    struct ds *ds = s->ds;
 
     switch (json->type) {
     case JSON_NULL:
@@ -1409,11 +1498,11 @@ json_to_ds(const struct json *json, struct json_serializer *s)
         break;
 
     case JSON_OBJECT:
-        json_object_to_ds(json->u.object, s);
+        json_serialize_object(json->u.object, s);
         break;
 
     case JSON_ARRAY:
-        json_array_to_ds(&json->u.array, s);
+        json_serialize_array(&json->u.array, s);
         break;
 
     case JSON_INTEGER:
@@ -1425,12 +1514,12 @@ json_to_ds(const struct json *json, struct json_serializer *s)
         break;
 
     case JSON_STRING:
-        json_string_to_ds(json->u.string, ds);
+        json_serialize_string(json->u.string, ds);
         break;
 
     case JSON_N_TYPES:
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -1438,34 +1527,34 @@ static void
 indent_line(struct json_serializer *s)
 {
     if (s->flags & JSSF_PRETTY) {
-        ds_put_char(&s->ds, '\n');
-        ds_put_char_multiple(&s->ds, ' ', SPACES_PER_LEVEL * s->depth);
+        ds_put_char(s->ds, '\n');
+        ds_put_char_multiple(s->ds, ' ', SPACES_PER_LEVEL * s->depth);
     }
 }
 
 static void
-json_object_member_to_ds(size_t i, const struct shash_node *node,
-                         struct json_serializer *s)
+json_serialize_object_member(size_t i, const struct shash_node *node,
+                             struct json_serializer *s)
 {
-    struct ds *ds = &s->ds;
+    struct ds *ds = s->ds;
 
     if (i) {
         ds_put_char(ds, ',');
         indent_line(s);
     }
 
-    json_string_to_ds(node->name, ds);
+    json_serialize_string(node->name, ds);
     ds_put_char(ds, ':');
     if (s->flags & JSSF_PRETTY) {
         ds_put_char(ds, ' ');
     }
-    json_to_ds(node->data, s);
+    json_serialize(node->data, s);
 }
 
 static void
-json_object_to_ds(const struct shash *object, struct json_serializer *s)
+json_serialize_object(const struct shash *object, struct json_serializer *s)
 {
-    struct ds *ds = &s->ds;
+    struct ds *ds = s->ds;
 
     ds_put_char(ds, '{');
 
@@ -1479,7 +1568,7 @@ json_object_to_ds(const struct shash *object, struct json_serializer *s)
         nodes = shash_sort(object);
         n = shash_count(object);
         for (i = 0; i < n; i++) {
-            json_object_member_to_ds(i, nodes[i], s);
+            json_serialize_object_member(i, nodes[i], s);
         }
         free(nodes);
     } else {
@@ -1488,7 +1577,7 @@ json_object_to_ds(const struct shash *object, struct json_serializer *s)
 
         i = 0;
         SHASH_FOR_EACH (node, object) {
-            json_object_member_to_ds(i++, node, s);
+            json_serialize_object_member(i++, node, s);
         }
     }
 
@@ -1497,9 +1586,9 @@ json_object_to_ds(const struct shash *object, struct json_serializer *s)
 }
 
 static void
-json_array_to_ds(const struct json_array *array, struct json_serializer *s)
+json_serialize_array(const struct json_array *array, struct json_serializer *s)
 {
-    struct ds *ds = &s->ds;
+    struct ds *ds = s->ds;
     size_t i;
 
     ds_put_char(ds, '[');
@@ -1513,7 +1602,7 @@ json_array_to_ds(const struct json_array *array, struct json_serializer *s)
                 ds_put_char(ds, ',');
                 indent_line(s);
             }
-            json_to_ds(array->elems[i], s);
+            json_serialize(array->elems[i], s);
         }
     }
 
@@ -1522,7 +1611,7 @@ json_array_to_ds(const struct json_array *array, struct json_serializer *s)
 }
 
 static void
-json_string_to_ds(const char *string, struct ds *ds)
+json_serialize_string(const char *string, struct ds *ds)
 {
     uint8_t c;