json-writer.con commit Merge branch 'sg/travis-osx-brew-breakage-workaround' (d219b7f)
   1#include "cache.h"
   2#include "json-writer.h"
   3
   4void jw_init(struct json_writer *jw)
   5{
   6        strbuf_init(&jw->json, 0);
   7        strbuf_init(&jw->open_stack, 0);
   8        jw->need_comma = 0;
   9        jw->pretty = 0;
  10}
  11
  12void jw_release(struct json_writer *jw)
  13{
  14        strbuf_release(&jw->json);
  15        strbuf_release(&jw->open_stack);
  16}
  17
  18/*
  19 * Append JSON-quoted version of the given string to 'out'.
  20 */
  21static void append_quoted_string(struct strbuf *out, const char *in)
  22{
  23        unsigned char c;
  24
  25        strbuf_addch(out, '"');
  26        while ((c = *in++) != '\0') {
  27                if (c == '"')
  28                        strbuf_addstr(out, "\\\"");
  29                else if (c == '\\')
  30                        strbuf_addstr(out, "\\\\");
  31                else if (c == '\n')
  32                        strbuf_addstr(out, "\\n");
  33                else if (c == '\r')
  34                        strbuf_addstr(out, "\\r");
  35                else if (c == '\t')
  36                        strbuf_addstr(out, "\\t");
  37                else if (c == '\f')
  38                        strbuf_addstr(out, "\\f");
  39                else if (c == '\b')
  40                        strbuf_addstr(out, "\\b");
  41                else if (c < 0x20)
  42                        strbuf_addf(out, "\\u%04x", c);
  43                else
  44                        strbuf_addch(out, c);
  45        }
  46        strbuf_addch(out, '"');
  47}
  48
  49static void indent_pretty(struct json_writer *jw)
  50{
  51        int k;
  52
  53        for (k = 0; k < jw->open_stack.len; k++)
  54                strbuf_addstr(&jw->json, "  ");
  55}
  56
  57/*
  58 * Begin an object or array (either top-level or nested within the currently
  59 * open object or array).
  60 */
  61static void begin(struct json_writer *jw, char ch_open, int pretty)
  62{
  63        jw->pretty = pretty;
  64
  65        strbuf_addch(&jw->json, ch_open);
  66
  67        strbuf_addch(&jw->open_stack, ch_open);
  68        jw->need_comma = 0;
  69}
  70
  71/*
  72 * Assert that the top of the open-stack is an object.
  73 */
  74static void assert_in_object(const struct json_writer *jw, const char *key)
  75{
  76        if (!jw->open_stack.len)
  77                BUG("json-writer: object: missing jw_object_begin(): '%s'", key);
  78        if (jw->open_stack.buf[jw->open_stack.len - 1] != '{')
  79                BUG("json-writer: object: not in object: '%s'", key);
  80}
  81
  82/*
  83 * Assert that the top of the open-stack is an array.
  84 */
  85static void assert_in_array(const struct json_writer *jw)
  86{
  87        if (!jw->open_stack.len)
  88                BUG("json-writer: array: missing jw_array_begin()");
  89        if (jw->open_stack.buf[jw->open_stack.len - 1] != '[')
  90                BUG("json-writer: array: not in array");
  91}
  92
  93/*
  94 * Add comma if we have already seen a member at this level.
  95 */
  96static void maybe_add_comma(struct json_writer *jw)
  97{
  98        if (jw->need_comma)
  99                strbuf_addch(&jw->json, ',');
 100        else
 101                jw->need_comma = 1;
 102}
 103
 104static void fmt_double(struct json_writer *jw, int precision,
 105                              double value)
 106{
 107        if (precision < 0) {
 108                strbuf_addf(&jw->json, "%f", value);
 109        } else {
 110                struct strbuf fmt = STRBUF_INIT;
 111                strbuf_addf(&fmt, "%%.%df", precision);
 112                strbuf_addf(&jw->json, fmt.buf, value);
 113                strbuf_release(&fmt);
 114        }
 115}
 116
 117static void object_common(struct json_writer *jw, const char *key)
 118{
 119        assert_in_object(jw, key);
 120        maybe_add_comma(jw);
 121
 122        if (jw->pretty) {
 123                strbuf_addch(&jw->json, '\n');
 124                indent_pretty(jw);
 125        }
 126
 127        append_quoted_string(&jw->json, key);
 128        strbuf_addch(&jw->json, ':');
 129        if (jw->pretty)
 130                strbuf_addch(&jw->json, ' ');
 131}
 132
 133static void array_common(struct json_writer *jw)
 134{
 135        assert_in_array(jw);
 136        maybe_add_comma(jw);
 137
 138        if (jw->pretty) {
 139                strbuf_addch(&jw->json, '\n');
 140                indent_pretty(jw);
 141        }
 142}
 143
 144/*
 145 * Assert that the given JSON object or JSON array has been properly
 146 * terminated.  (Has closing bracket.)
 147 */
 148static void assert_is_terminated(const struct json_writer *jw)
 149{
 150        if (jw->open_stack.len)
 151                BUG("json-writer: object: missing jw_end(): '%s'",
 152                    jw->json.buf);
 153}
 154
 155void jw_object_begin(struct json_writer *jw, int pretty)
 156{
 157        begin(jw, '{', pretty);
 158}
 159
 160void jw_object_string(struct json_writer *jw, const char *key, const char *value)
 161{
 162        object_common(jw, key);
 163        append_quoted_string(&jw->json, value);
 164}
 165
 166void jw_object_intmax(struct json_writer *jw, const char *key, intmax_t value)
 167{
 168        object_common(jw, key);
 169        strbuf_addf(&jw->json, "%"PRIdMAX, value);
 170}
 171
 172void jw_object_double(struct json_writer *jw, const char *key, int precision,
 173                      double value)
 174{
 175        object_common(jw, key);
 176        fmt_double(jw, precision, value);
 177}
 178
 179void jw_object_true(struct json_writer *jw, const char *key)
 180{
 181        object_common(jw, key);
 182        strbuf_addstr(&jw->json, "true");
 183}
 184
 185void jw_object_false(struct json_writer *jw, const char *key)
 186{
 187        object_common(jw, key);
 188        strbuf_addstr(&jw->json, "false");
 189}
 190
 191void jw_object_bool(struct json_writer *jw, const char *key, int value)
 192{
 193        if (value)
 194                jw_object_true(jw, key);
 195        else
 196                jw_object_false(jw, key);
 197}
 198
 199void jw_object_null(struct json_writer *jw, const char *key)
 200{
 201        object_common(jw, key);
 202        strbuf_addstr(&jw->json, "null");
 203}
 204
 205static void increase_indent(struct strbuf *sb,
 206                            const struct json_writer *jw,
 207                            int indent)
 208{
 209        int k;
 210
 211        strbuf_reset(sb);
 212        for (k = 0; k < jw->json.len; k++) {
 213                char ch = jw->json.buf[k];
 214                strbuf_addch(sb, ch);
 215                if (ch == '\n')
 216                        strbuf_addchars(sb, ' ', indent);
 217        }
 218}
 219
 220static void kill_indent(struct strbuf *sb,
 221                        const struct json_writer *jw)
 222{
 223        int k;
 224        int eat_it = 0;
 225
 226        strbuf_reset(sb);
 227        for (k = 0; k < jw->json.len; k++) {
 228                char ch = jw->json.buf[k];
 229                if (eat_it && ch == ' ')
 230                        continue;
 231                if (ch == '\n') {
 232                        eat_it = 1;
 233                        continue;
 234                }
 235                eat_it = 0;
 236                strbuf_addch(sb, ch);
 237        }
 238}
 239
 240static void append_sub_jw(struct json_writer *jw,
 241                          const struct json_writer *value)
 242{
 243        /*
 244         * If both are pretty, increase the indentation of the sub_jw
 245         * to better fit under the super.
 246         *
 247         * If the super is pretty, but the sub_jw is compact, leave the
 248         * sub_jw compact.  (We don't want to parse and rebuild the sub_jw
 249         * for this debug-ish feature.)
 250         *
 251         * If the super is compact, and the sub_jw is pretty, convert
 252         * the sub_jw to compact.
 253         *
 254         * If both are compact, keep the sub_jw compact.
 255         */
 256        if (jw->pretty && jw->open_stack.len && value->pretty) {
 257                struct strbuf sb = STRBUF_INIT;
 258                increase_indent(&sb, value, jw->open_stack.len * 2);
 259                strbuf_addbuf(&jw->json, &sb);
 260                strbuf_release(&sb);
 261                return;
 262        }
 263        if (!jw->pretty && value->pretty) {
 264                struct strbuf sb = STRBUF_INIT;
 265                kill_indent(&sb, value);
 266                strbuf_addbuf(&jw->json, &sb);
 267                strbuf_release(&sb);
 268                return;
 269        }
 270
 271        strbuf_addbuf(&jw->json, &value->json);
 272}
 273
 274/*
 275 * Append existing (properly terminated) JSON sub-data (object or array)
 276 * as-is onto the given JSON data.
 277 */
 278void jw_object_sub_jw(struct json_writer *jw, const char *key,
 279                      const struct json_writer *value)
 280{
 281        assert_is_terminated(value);
 282
 283        object_common(jw, key);
 284        append_sub_jw(jw, value);
 285}
 286
 287void jw_object_inline_begin_object(struct json_writer *jw, const char *key)
 288{
 289        object_common(jw, key);
 290
 291        jw_object_begin(jw, jw->pretty);
 292}
 293
 294void jw_object_inline_begin_array(struct json_writer *jw, const char *key)
 295{
 296        object_common(jw, key);
 297
 298        jw_array_begin(jw, jw->pretty);
 299}
 300
 301void jw_array_begin(struct json_writer *jw, int pretty)
 302{
 303        begin(jw, '[', pretty);
 304}
 305
 306void jw_array_string(struct json_writer *jw, const char *value)
 307{
 308        array_common(jw);
 309        append_quoted_string(&jw->json, value);
 310}
 311
 312void jw_array_intmax(struct json_writer *jw, intmax_t value)
 313{
 314        array_common(jw);
 315        strbuf_addf(&jw->json, "%"PRIdMAX, value);
 316}
 317
 318void jw_array_double(struct json_writer *jw, int precision, double value)
 319{
 320        array_common(jw);
 321        fmt_double(jw, precision, value);
 322}
 323
 324void jw_array_true(struct json_writer *jw)
 325{
 326        array_common(jw);
 327        strbuf_addstr(&jw->json, "true");
 328}
 329
 330void jw_array_false(struct json_writer *jw)
 331{
 332        array_common(jw);
 333        strbuf_addstr(&jw->json, "false");
 334}
 335
 336void jw_array_bool(struct json_writer *jw, int value)
 337{
 338        if (value)
 339                jw_array_true(jw);
 340        else
 341                jw_array_false(jw);
 342}
 343
 344void jw_array_null(struct json_writer *jw)
 345{
 346        array_common(jw);
 347        strbuf_addstr(&jw->json, "null");
 348}
 349
 350void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value)
 351{
 352        assert_is_terminated(value);
 353
 354        array_common(jw);
 355        append_sub_jw(jw, value);
 356}
 357
 358void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv)
 359{
 360        int k;
 361
 362        for (k = 0; k < argc; k++)
 363                jw_array_string(jw, argv[k]);
 364}
 365
 366void jw_array_argv(struct json_writer *jw, const char **argv)
 367{
 368        while (*argv)
 369                jw_array_string(jw, *argv++);
 370}
 371
 372void jw_array_inline_begin_object(struct json_writer *jw)
 373{
 374        array_common(jw);
 375
 376        jw_object_begin(jw, jw->pretty);
 377}
 378
 379void jw_array_inline_begin_array(struct json_writer *jw)
 380{
 381        array_common(jw);
 382
 383        jw_array_begin(jw, jw->pretty);
 384}
 385
 386int jw_is_terminated(const struct json_writer *jw)
 387{
 388        return !jw->open_stack.len;
 389}
 390
 391void jw_end(struct json_writer *jw)
 392{
 393        char ch_open;
 394        int len;
 395
 396        if (!jw->open_stack.len)
 397                BUG("json-writer: too many jw_end(): '%s'", jw->json.buf);
 398
 399        len = jw->open_stack.len - 1;
 400        ch_open = jw->open_stack.buf[len];
 401
 402        strbuf_setlen(&jw->open_stack, len);
 403        jw->need_comma = 1;
 404
 405        if (jw->pretty) {
 406                strbuf_addch(&jw->json, '\n');
 407                indent_pretty(jw);
 408        }
 409
 410        if (ch_open == '{')
 411                strbuf_addch(&jw->json, '}');
 412        else
 413                strbuf_addch(&jw->json, ']');
 414}