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