mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
6.7 KiB
358 lines
6.7 KiB
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) |
|
/* |
|
* Simple streaming JSON writer |
|
* |
|
* This takes care of the annoying bits of JSON syntax like the commas |
|
* after elements |
|
* |
|
* Authors: Stephen Hemminger <[email protected]> |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdbool.h> |
|
#include <stdarg.h> |
|
#include <assert.h> |
|
#include <malloc.h> |
|
#include <inttypes.h> |
|
#include <stdint.h> |
|
|
|
#include "json_writer.h" |
|
|
|
struct json_writer { |
|
FILE *out; /* output file */ |
|
unsigned depth; /* nesting */ |
|
bool pretty; /* optional whitepace */ |
|
char sep; /* either nul or comma */ |
|
}; |
|
|
|
/* indentation for pretty print */ |
|
static void jsonw_indent(json_writer_t *self) |
|
{ |
|
unsigned i; |
|
for (i = 0; i < self->depth; ++i) |
|
fputs(" ", self->out); |
|
} |
|
|
|
/* end current line and indent if pretty printing */ |
|
static void jsonw_eol(json_writer_t *self) |
|
{ |
|
if (!self->pretty) |
|
return; |
|
|
|
putc('\n', self->out); |
|
jsonw_indent(self); |
|
} |
|
|
|
/* If current object is not empty print a comma */ |
|
static void jsonw_eor(json_writer_t *self) |
|
{ |
|
if (self->sep != '\0') |
|
putc(self->sep, self->out); |
|
self->sep = ','; |
|
} |
|
|
|
|
|
/* Output JSON encoded string */ |
|
/* Handles C escapes, does not do Unicode */ |
|
static void jsonw_puts(json_writer_t *self, const char *str) |
|
{ |
|
putc('"', self->out); |
|
for (; *str; ++str) |
|
switch (*str) { |
|
case '\t': |
|
fputs("\\t", self->out); |
|
break; |
|
case '\n': |
|
fputs("\\n", self->out); |
|
break; |
|
case '\r': |
|
fputs("\\r", self->out); |
|
break; |
|
case '\f': |
|
fputs("\\f", self->out); |
|
break; |
|
case '\b': |
|
fputs("\\b", self->out); |
|
break; |
|
case '\\': |
|
fputs("\\n", self->out); |
|
break; |
|
case '"': |
|
fputs("\\\"", self->out); |
|
break; |
|
case '\'': |
|
fputs("\\\'", self->out); |
|
break; |
|
default: |
|
putc(*str, self->out); |
|
} |
|
putc('"', self->out); |
|
} |
|
|
|
/* Create a new JSON stream */ |
|
json_writer_t *jsonw_new(FILE *f) |
|
{ |
|
json_writer_t *self = malloc(sizeof(*self)); |
|
if (self) { |
|
self->out = f; |
|
self->depth = 0; |
|
self->pretty = false; |
|
self->sep = '\0'; |
|
} |
|
return self; |
|
} |
|
|
|
/* End output to JSON stream */ |
|
void jsonw_destroy(json_writer_t **self_p) |
|
{ |
|
json_writer_t *self = *self_p; |
|
|
|
assert(self->depth == 0); |
|
fputs("\n", self->out); |
|
fflush(self->out); |
|
free(self); |
|
*self_p = NULL; |
|
} |
|
|
|
void jsonw_pretty(json_writer_t *self, bool on) |
|
{ |
|
self->pretty = on; |
|
} |
|
|
|
void jsonw_reset(json_writer_t *self) |
|
{ |
|
assert(self->depth == 0); |
|
self->sep = '\0'; |
|
} |
|
|
|
/* Basic blocks */ |
|
static void jsonw_begin(json_writer_t *self, int c) |
|
{ |
|
jsonw_eor(self); |
|
putc(c, self->out); |
|
++self->depth; |
|
self->sep = '\0'; |
|
} |
|
|
|
static void jsonw_end(json_writer_t *self, int c) |
|
{ |
|
assert(self->depth > 0); |
|
|
|
--self->depth; |
|
if (self->sep != '\0') |
|
jsonw_eol(self); |
|
putc(c, self->out); |
|
self->sep = ','; |
|
} |
|
|
|
|
|
/* Add a JSON property name */ |
|
void jsonw_name(json_writer_t *self, const char *name) |
|
{ |
|
jsonw_eor(self); |
|
jsonw_eol(self); |
|
self->sep = '\0'; |
|
jsonw_puts(self, name); |
|
putc(':', self->out); |
|
if (self->pretty) |
|
putc(' ', self->out); |
|
} |
|
|
|
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap) |
|
{ |
|
jsonw_eor(self); |
|
putc('"', self->out); |
|
vfprintf(self->out, fmt, ap); |
|
putc('"', self->out); |
|
} |
|
|
|
void jsonw_printf(json_writer_t *self, const char *fmt, ...) |
|
{ |
|
va_list ap; |
|
|
|
va_start(ap, fmt); |
|
jsonw_eor(self); |
|
vfprintf(self->out, fmt, ap); |
|
va_end(ap); |
|
} |
|
|
|
/* Collections */ |
|
void jsonw_start_object(json_writer_t *self) |
|
{ |
|
jsonw_begin(self, '{'); |
|
} |
|
|
|
void jsonw_end_object(json_writer_t *self) |
|
{ |
|
jsonw_end(self, '}'); |
|
} |
|
|
|
void jsonw_start_array(json_writer_t *self) |
|
{ |
|
jsonw_begin(self, '['); |
|
} |
|
|
|
void jsonw_end_array(json_writer_t *self) |
|
{ |
|
jsonw_end(self, ']'); |
|
} |
|
|
|
/* JSON value types */ |
|
void jsonw_string(json_writer_t *self, const char *value) |
|
{ |
|
jsonw_eor(self); |
|
jsonw_puts(self, value); |
|
} |
|
|
|
void jsonw_bool(json_writer_t *self, bool val) |
|
{ |
|
jsonw_printf(self, "%s", val ? "true" : "false"); |
|
} |
|
|
|
void jsonw_null(json_writer_t *self) |
|
{ |
|
jsonw_printf(self, "null"); |
|
} |
|
|
|
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num) |
|
{ |
|
jsonw_printf(self, fmt, num); |
|
} |
|
|
|
#ifdef notused |
|
void jsonw_float(json_writer_t *self, double num) |
|
{ |
|
jsonw_printf(self, "%g", num); |
|
} |
|
#endif |
|
|
|
void jsonw_hu(json_writer_t *self, unsigned short num) |
|
{ |
|
jsonw_printf(self, "%hu", num); |
|
} |
|
|
|
void jsonw_uint(json_writer_t *self, uint64_t num) |
|
{ |
|
jsonw_printf(self, "%"PRIu64, num); |
|
} |
|
|
|
void jsonw_lluint(json_writer_t *self, unsigned long long int num) |
|
{ |
|
jsonw_printf(self, "%llu", num); |
|
} |
|
|
|
void jsonw_int(json_writer_t *self, int64_t num) |
|
{ |
|
jsonw_printf(self, "%"PRId64, num); |
|
} |
|
|
|
/* Basic name/value objects */ |
|
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_string(self, val); |
|
} |
|
|
|
void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_bool(self, val); |
|
} |
|
|
|
#ifdef notused |
|
void jsonw_float_field(json_writer_t *self, const char *prop, double val) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_float(self, val); |
|
} |
|
#endif |
|
|
|
void jsonw_float_field_fmt(json_writer_t *self, |
|
const char *prop, |
|
const char *fmt, |
|
double val) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_float_fmt(self, fmt, val); |
|
} |
|
|
|
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_uint(self, num); |
|
} |
|
|
|
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_hu(self, num); |
|
} |
|
|
|
void jsonw_lluint_field(json_writer_t *self, |
|
const char *prop, |
|
unsigned long long int num) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_lluint(self, num); |
|
} |
|
|
|
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_int(self, num); |
|
} |
|
|
|
void jsonw_null_field(json_writer_t *self, const char *prop) |
|
{ |
|
jsonw_name(self, prop); |
|
jsonw_null(self); |
|
} |
|
|
|
#ifdef TEST |
|
int main(int argc, char **argv) |
|
{ |
|
json_writer_t *wr = jsonw_new(stdout); |
|
|
|
jsonw_start_object(wr); |
|
jsonw_pretty(wr, true); |
|
jsonw_name(wr, "Vyatta"); |
|
jsonw_start_object(wr); |
|
jsonw_string_field(wr, "url", "http://vyatta.com"); |
|
jsonw_uint_field(wr, "downloads", 2000000ul); |
|
jsonw_float_field(wr, "stock", 8.16); |
|
|
|
jsonw_name(wr, "ARGV"); |
|
jsonw_start_array(wr); |
|
while (--argc) |
|
jsonw_string(wr, *++argv); |
|
jsonw_end_array(wr); |
|
|
|
jsonw_name(wr, "empty"); |
|
jsonw_start_array(wr); |
|
jsonw_end_array(wr); |
|
|
|
jsonw_name(wr, "NIL"); |
|
jsonw_start_object(wr); |
|
jsonw_end_object(wr); |
|
|
|
jsonw_null_field(wr, "my_null"); |
|
|
|
jsonw_name(wr, "special chars"); |
|
jsonw_start_array(wr); |
|
jsonw_string_field(wr, "slash", "/"); |
|
jsonw_string_field(wr, "newline", "\n"); |
|
jsonw_string_field(wr, "tab", "\t"); |
|
jsonw_string_field(wr, "ff", "\f"); |
|
jsonw_string_field(wr, "quote", "\""); |
|
jsonw_string_field(wr, "tick", "\'"); |
|
jsonw_string_field(wr, "backslash", "\\"); |
|
jsonw_end_array(wr); |
|
|
|
jsonw_end_object(wr); |
|
|
|
jsonw_end_object(wr); |
|
jsonw_destroy(&wr); |
|
return 0; |
|
} |
|
|
|
#endif
|
|
|