1 | /* |
2 | * Copyright © 2021 Benjamin Otte |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Authors: Benjamin Otte <otte@gnome.org> |
18 | */ |
19 | |
20 | |
21 | #include "config.h" |
22 | |
23 | #include "gtkjsonprinterprivate.h" |
24 | |
25 | typedef struct _GtkJsonBlock GtkJsonBlock; |
26 | |
27 | typedef enum { |
28 | GTK_JSON_BLOCK_TOPLEVEL, |
29 | GTK_JSON_BLOCK_OBJECT, |
30 | GTK_JSON_BLOCK_ARRAY, |
31 | } GtkJsonBlockType; |
32 | |
33 | struct _GtkJsonBlock |
34 | { |
35 | GtkJsonBlockType type; |
36 | gsize n_elements; /* number of elements already written */ |
37 | }; |
38 | |
39 | struct _GtkJsonPrinter |
40 | { |
41 | GtkJsonPrinterFlags flags; |
42 | char *indentation; |
43 | |
44 | GtkJsonPrinterWriteFunc write_func; |
45 | gpointer user_data; |
46 | GDestroyNotify user_destroy; |
47 | |
48 | GtkJsonBlock *block; /* current block */ |
49 | GtkJsonBlock *blocks; /* blocks array */ |
50 | GtkJsonBlock *blocks_end; /* blocks array */ |
51 | GtkJsonBlock blocks_preallocated[128]; /* preallocated */ |
52 | }; |
53 | |
54 | static void |
55 | gtk_json_printer_push_block (GtkJsonPrinter *self, |
56 | GtkJsonBlockType type) |
57 | { |
58 | self->block++; |
59 | if (self->block == self->blocks_end) |
60 | { |
61 | gsize old_size = self->blocks_end - self->blocks; |
62 | gsize new_size = old_size + 128; |
63 | |
64 | if (self->blocks == self->blocks_preallocated) |
65 | { |
66 | self->blocks = g_new (GtkJsonBlock, new_size); |
67 | memcpy (dest: self->blocks, src: self->blocks_preallocated, n: sizeof (GtkJsonBlock) * G_N_ELEMENTS (self->blocks_preallocated)); |
68 | } |
69 | else |
70 | { |
71 | self->blocks = g_renew (GtkJsonBlock, self->blocks, new_size); |
72 | } |
73 | self->blocks_end = self->blocks + new_size; |
74 | self->block = self->blocks + old_size; |
75 | } |
76 | |
77 | self->block->type = type; |
78 | self->block->n_elements = 0; |
79 | } |
80 | |
81 | static void |
82 | gtk_json_printer_pop_block (GtkJsonPrinter *self) |
83 | { |
84 | g_assert (self->block > self->blocks); |
85 | self->block--; |
86 | } |
87 | |
88 | GtkJsonPrinter * |
89 | gtk_json_printer_new (GtkJsonPrinterWriteFunc write_func, |
90 | gpointer data, |
91 | GDestroyNotify destroy) |
92 | { |
93 | GtkJsonPrinter *self; |
94 | |
95 | g_return_val_if_fail (write_func, NULL); |
96 | |
97 | self = g_slice_new0 (GtkJsonPrinter); |
98 | |
99 | self->flags = 0; |
100 | self->indentation = g_strdup (str: " " ); |
101 | |
102 | self->write_func = write_func; |
103 | self->user_data = data; |
104 | self->user_destroy = destroy; |
105 | |
106 | self->blocks = self->blocks_preallocated; |
107 | self->blocks_end = self->blocks + G_N_ELEMENTS (self->blocks_preallocated); |
108 | self->block = self->blocks; |
109 | self->block->type = GTK_JSON_BLOCK_TOPLEVEL; |
110 | |
111 | return self; |
112 | } |
113 | |
114 | void |
115 | gtk_json_printer_free (GtkJsonPrinter *self) |
116 | { |
117 | g_return_if_fail (self != NULL); |
118 | |
119 | g_free (mem: self->indentation); |
120 | |
121 | if (self->user_destroy) |
122 | self->user_destroy (self->user_data); |
123 | |
124 | if (self->blocks != self->blocks_preallocated) |
125 | g_free (mem: self->blocks); |
126 | |
127 | g_slice_free (GtkJsonPrinter, self); |
128 | } |
129 | |
130 | static gboolean |
131 | gtk_json_printer_has_flag (GtkJsonPrinter *self, |
132 | GtkJsonPrinterFlags flag) |
133 | { |
134 | return (self->flags & flag) ? TRUE : FALSE; |
135 | } |
136 | |
137 | gsize |
138 | gtk_json_printer_get_depth (GtkJsonPrinter *self) |
139 | { |
140 | return self->block - self->blocks; |
141 | } |
142 | |
143 | gsize |
144 | gtk_json_printer_get_n_elements (GtkJsonPrinter *self) |
145 | { |
146 | return self->block->n_elements; |
147 | } |
148 | |
149 | void |
150 | gtk_json_printer_set_flags (GtkJsonPrinter *self, |
151 | GtkJsonPrinterFlags flags) |
152 | { |
153 | g_return_if_fail (self != NULL); |
154 | |
155 | self->flags = flags; |
156 | } |
157 | |
158 | GtkJsonPrinterFlags |
159 | gtk_json_printer_get_flags (GtkJsonPrinter *self) |
160 | { |
161 | g_return_val_if_fail (self != NULL, 0); |
162 | |
163 | return self->flags; |
164 | } |
165 | |
166 | void |
167 | gtk_json_printer_set_indentation (GtkJsonPrinter *self, |
168 | gsize amount) |
169 | { |
170 | g_return_if_fail (self != NULL); |
171 | |
172 | g_free (mem: self->indentation); |
173 | |
174 | self->indentation = g_malloc (n_bytes: amount + 1); |
175 | memset (s: self->indentation, c: ' ', n: amount); |
176 | self->indentation[amount] = 0; |
177 | } |
178 | |
179 | gsize |
180 | gtk_json_printer_get_indentation (GtkJsonPrinter *self) |
181 | { |
182 | g_return_val_if_fail (self != NULL, 2); |
183 | |
184 | return strlen (s: self->indentation); |
185 | } |
186 | |
187 | static void |
188 | gtk_json_printer_write (GtkJsonPrinter *self, |
189 | const char *s) |
190 | { |
191 | self->write_func (self, s, self->user_data); |
192 | } |
193 | |
194 | static char * |
195 | gtk_json_printer_escape_string (GtkJsonPrinter *self, |
196 | const char *str) |
197 | { |
198 | GString *string; |
199 | |
200 | string = g_string_new (NULL); |
201 | string = g_string_append_c (string, '"'); |
202 | |
203 | for (; *str != '\0'; str = g_utf8_next_char (str)) |
204 | { |
205 | switch (*str) |
206 | { |
207 | case '"': |
208 | g_string_append (string, val: "\\\"" ); |
209 | break; |
210 | case '\\': |
211 | g_string_append (string, val: "\\\\" ); |
212 | break; |
213 | case '\b': |
214 | g_string_append (string, val: "\\b" ); |
215 | break; |
216 | case '\f': |
217 | g_string_append (string, val: "\\f" ); |
218 | break; |
219 | case '\n': |
220 | g_string_append (string, val: "\\n" ); |
221 | break; |
222 | case '\r': |
223 | g_string_append (string, val: "\\r" ); |
224 | break; |
225 | case '\t': |
226 | g_string_append (string, val: "\\t" ); |
227 | break; |
228 | default: |
229 | if ((int) *str < 0x20 || (int) *str >= 0x80) |
230 | { |
231 | if ((guint) *str < 0x20 || gtk_json_printer_has_flag (self, flag: GTK_JSON_PRINTER_ASCII)) |
232 | g_string_append_printf (string, format: "\\u%04x" , g_utf8_get_char (p: str)); |
233 | else |
234 | g_string_append_unichar (string, wc: g_utf8_get_char (p: str)); |
235 | } |
236 | else |
237 | g_string_append_c (string, *str); |
238 | } |
239 | } |
240 | |
241 | string = g_string_append_c (string, '"'); |
242 | return g_string_free (string, FALSE); |
243 | } |
244 | |
245 | static void |
246 | gtk_json_printer_newline (GtkJsonPrinter *self) |
247 | { |
248 | gsize depth; |
249 | |
250 | if (!gtk_json_printer_has_flag (self, flag: GTK_JSON_PRINTER_PRETTY)) |
251 | return; |
252 | |
253 | gtk_json_printer_write (self, s: "\n" ); |
254 | for (depth = gtk_json_printer_get_depth (self); depth-->0;) |
255 | gtk_json_printer_write (self, s: self->indentation); |
256 | } |
257 | |
258 | static void |
259 | gtk_json_printer_begin_member (GtkJsonPrinter *self, |
260 | const char *name) |
261 | { |
262 | if (gtk_json_printer_get_n_elements (self) > 0) |
263 | gtk_json_printer_write (self, s: "," ); |
264 | if (self->block->type != GTK_JSON_BLOCK_TOPLEVEL || gtk_json_printer_get_n_elements (self) > 0) |
265 | gtk_json_printer_newline (self); |
266 | |
267 | self->block->n_elements++; |
268 | |
269 | if (name) |
270 | { |
271 | char *escaped = gtk_json_printer_escape_string (self, str: name); |
272 | gtk_json_printer_write (self, s: escaped); |
273 | g_free (mem: escaped); |
274 | if (gtk_json_printer_has_flag (self, flag: GTK_JSON_PRINTER_PRETTY)) |
275 | gtk_json_printer_write (self, s: " : " ); |
276 | else |
277 | gtk_json_printer_write (self, s: ":" ); |
278 | } |
279 | } |
280 | |
281 | void |
282 | gtk_json_printer_add_boolean (GtkJsonPrinter *self, |
283 | const char *name, |
284 | gboolean value) |
285 | { |
286 | g_return_if_fail (self != NULL); |
287 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
288 | |
289 | gtk_json_printer_begin_member (self, name); |
290 | gtk_json_printer_write (self, s: value ? "true" : "false" ); |
291 | } |
292 | |
293 | void |
294 | gtk_json_printer_add_number (GtkJsonPrinter *self, |
295 | const char *name, |
296 | double value) |
297 | { |
298 | char buf[G_ASCII_DTOSTR_BUF_SIZE]; |
299 | |
300 | g_return_if_fail (self != NULL); |
301 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
302 | |
303 | gtk_json_printer_begin_member (self, name); |
304 | g_ascii_dtostr (buffer: buf, G_ASCII_DTOSTR_BUF_SIZE, d: value); |
305 | gtk_json_printer_write (self, s: buf); |
306 | } |
307 | |
308 | void |
309 | gtk_json_printer_add_integer (GtkJsonPrinter *self, |
310 | const char *name, |
311 | int value) |
312 | { |
313 | char buf[128]; |
314 | |
315 | g_return_if_fail (self != NULL); |
316 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
317 | |
318 | gtk_json_printer_begin_member (self, name); |
319 | g_snprintf (string: buf, n: sizeof (buf), format: "%d" , value); |
320 | gtk_json_printer_write (self, s: buf); |
321 | } |
322 | |
323 | void |
324 | gtk_json_printer_add_string (GtkJsonPrinter *self, |
325 | const char *name, |
326 | const char *s) |
327 | { |
328 | char *escaped; |
329 | |
330 | g_return_if_fail (self != NULL); |
331 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
332 | g_return_if_fail (s != NULL); |
333 | |
334 | gtk_json_printer_begin_member (self, name); |
335 | escaped = gtk_json_printer_escape_string (self, str: s); |
336 | gtk_json_printer_write (self, s: escaped); |
337 | g_free (mem: escaped); |
338 | } |
339 | |
340 | void |
341 | gtk_json_printer_add_null (GtkJsonPrinter *self, |
342 | const char *name) |
343 | { |
344 | g_return_if_fail (self != NULL); |
345 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
346 | |
347 | gtk_json_printer_begin_member (self, name); |
348 | gtk_json_printer_write (self, s: "null" ); |
349 | } |
350 | |
351 | void |
352 | gtk_json_printer_start_object (GtkJsonPrinter *self, |
353 | const char *name) |
354 | { |
355 | g_return_if_fail (self != NULL); |
356 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
357 | |
358 | gtk_json_printer_begin_member (self, name); |
359 | gtk_json_printer_write (self, s: "{" ); |
360 | gtk_json_printer_push_block (self, type: GTK_JSON_BLOCK_OBJECT); |
361 | } |
362 | |
363 | void |
364 | gtk_json_printer_start_array (GtkJsonPrinter *self, |
365 | const char *name) |
366 | { |
367 | g_return_if_fail (self != NULL); |
368 | g_return_if_fail ((self->block->type == GTK_JSON_BLOCK_OBJECT) == (name != NULL)); |
369 | |
370 | gtk_json_printer_begin_member (self, name); |
371 | gtk_json_printer_write (self, s: "[" ); |
372 | gtk_json_printer_push_block (self, type: GTK_JSON_BLOCK_ARRAY); |
373 | } |
374 | |
375 | void |
376 | gtk_json_printer_end (GtkJsonPrinter *self) |
377 | { |
378 | const char *bracket; |
379 | gboolean empty; |
380 | |
381 | g_return_if_fail (self != NULL); |
382 | |
383 | switch (self->block->type) |
384 | { |
385 | case GTK_JSON_BLOCK_OBJECT: |
386 | bracket = "}" ; |
387 | break; |
388 | case GTK_JSON_BLOCK_ARRAY: |
389 | bracket = "]" ; |
390 | break; |
391 | case GTK_JSON_BLOCK_TOPLEVEL: |
392 | default: |
393 | g_return_if_reached (); |
394 | } |
395 | |
396 | empty = gtk_json_printer_get_n_elements (self) == 0; |
397 | gtk_json_printer_pop_block (self); |
398 | |
399 | if (!empty) |
400 | { |
401 | gtk_json_printer_newline (self); |
402 | } |
403 | gtk_json_printer_write (self, s: bracket); |
404 | } |
405 | |
406 | |