]> mj.ucw.cz Git - libucw.git/blob - ucw-json/format.c
JSON: More tests and bug fixes
[libucw.git] / ucw-json / format.c
1 /*
2  *      UCW JSON Library -- Formatter
3  *
4  *      (c) 2015 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/fastbuf.h>
12 #include <ucw/gary.h>
13 #include <ucw/ff-unicode.h>
14 #include <ucw/unicode.h>
15 #include <ucw-json/json.h>
16
17 #include <float.h>
18 #include <math.h>
19 #include <stdio.h>
20
21 void json_set_output(struct json_context *js, struct fastbuf *fb)
22 {
23   js->out_fb = fb;
24 }
25
26 static void write_string(struct json_context *js, const char *p)
27 {
28   struct fastbuf *fb = js->out_fb;
29
30   bputc(fb, '"');
31   for (;;)
32     {
33       uint u;
34       p = utf8_32_get(p, &u);
35       if (!u)
36         break;
37       if (u == '"' || u == '\\')
38         {
39           bputc(fb, '\\');
40           bputc(fb, u);
41         }
42       else if (u < 0x20)
43         {
44           // We avoid "\f" nor "\b" and use "\uXXXX" instead
45           switch (u)
46             {
47             case 0x09: bputs(fb, "\\t"); break;
48             case 0x0a: bputs(fb, "\\n"); break;
49             case 0x0d: bputs(fb, "\\r"); break;
50             default:
51               bprintf(fb, "\\u%04x", u);
52             }
53         }
54       else if (u >= 0x007f && (js->format_options & JSON_FORMAT_ESCAPE_NONASCII))
55         {
56           if (u < 0x10000)
57             bprintf(fb, "\\u%04x", u);
58           else if (u < 0x110000)
59             bprintf(fb, "\\u%04x\\u%04x", 0xd800 + ((u - 0x10000) >> 10), 0xdc00 + (u & 0x3ff));
60           else
61             ASSERT(0);
62         }
63       else
64         bput_utf8_32(fb, u);
65     }
66   bputc(fb, '"');
67 }
68
69 static void write_number(struct fastbuf *fb, double val)
70 {
71   ASSERT(isfinite(val));
72   bprintf(fb, "%.*g", DBL_DIG+1, val);
73 }
74
75 static bool want_indent_p(struct json_context *js)
76 {
77   return (js->format_options & JSON_FORMAT_INDENT);
78 }
79
80 static void write_space(struct json_context *js)
81 {
82   struct fastbuf *fb = js->out_fb;
83
84   if (want_indent_p(js))
85     {
86       bputc(fb, '\n');
87       for (uint i=0; i < js->out_indent; i++)
88         bputc(fb, '\t');
89     }
90   else
91     bputc(fb, ' ');
92 }
93
94 void json_write_value(struct json_context *js, struct json_node *n)
95 {
96   struct fastbuf *fb = js->out_fb;
97
98   switch (n->type)
99     {
100     case JSON_NULL:
101       bputs(fb, "null");
102       break;
103     case JSON_BOOLEAN:
104       bputs(fb, (n->boolean ? "true" : "false"));
105       break;
106     case JSON_NUMBER:
107       write_number(fb, n->number);
108       break;
109     case JSON_STRING:
110       write_string(js, n->string);
111       break;
112     case JSON_ARRAY:
113       {
114         if (!GARY_SIZE(n->elements))
115           bputs(fb, "[]");
116         else
117           {
118             bputc(fb, '[');
119             js->out_indent++;
120             for (size_t i=0; i < GARY_SIZE(n->elements); i++)
121               {
122                 if (i)
123                   bputc(fb, ',');
124                 write_space(js);
125                 json_write_value(js, n->elements[i]);
126               }
127             js->out_indent--;
128             write_space(js);
129             bputc(fb, ']');
130           }
131         break;
132       }
133     case JSON_OBJECT:
134       {
135         if (!GARY_SIZE(n->pairs))
136           bputs(fb, "{}");
137         else
138           {
139             bputc(fb, '{');
140             js->out_indent++;
141             for (size_t i=0; i < GARY_SIZE(n->pairs); i++)
142               {
143                 if (i)
144                   bputc(fb, ',');
145                 write_space(js);
146                 struct json_pair *p = &n->pairs[i];
147                 write_string(js, p->key);
148                 bputs(fb, ": ");
149                 json_write_value(js, p->value);
150               }
151             js->out_indent--;
152             write_space(js);
153             bputc(fb, '}');
154           }
155         break;
156       }
157     default:
158       ASSERT(0);
159     }
160 }
161
162 void json_write(struct json_context *js, struct fastbuf *fb, struct json_node *n)
163 {
164   json_set_output(js, fb);
165   json_write_value(js, n);
166   bputc(fb, '\n');
167 }