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