]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - json.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / json.cpp
1 /*
2 * json.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2017-18 Christian Franke
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "config.h"
12 #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
13
14 #include "json.h"
15
16 const char * json_cvsid = "$Id: json.cpp 4830 2018-11-02 21:21:48Z chrfranke $"
17 JSON_H_CVSID;
18
19 #include "sg_unaligned.h"
20 #include "utility.h" // uint128_*()
21
22 #include <inttypes.h>
23 #include <stdexcept>
24
25 static void jassert_failed(int line, const char * expr)
26 {
27 char msg[128];
28 // Avoid __FILE__ as it may break reproducible builds
29 snprintf(msg, sizeof(msg), "json.cpp(%d): Assertion failed: %s", line, expr);
30 throw std::logic_error(msg);
31 }
32
33 #define jassert(expr) (!(expr) ? jassert_failed(__LINE__, #expr) : (void)0)
34
35 static void check_key(const char * key)
36 {
37 // Limit: object keys should be valid identifiers (lowercase only)
38 char c = key[0];
39 jassert('a' <= c && c <= 'z');
40 for (int i = 1; (c = key[i]); i++)
41 jassert(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || (c == '_'));
42 }
43
44 json::ref::ref(json & js, const char * key)
45 : m_js(js)
46 {
47 check_key(key);
48 m_path.push_back(node_info(key));
49 }
50
51 json::ref::ref(const ref & base, const char * key)
52 : m_js(base.m_js), m_path(base.m_path)
53 {
54 check_key(key);
55 m_path.push_back(node_info(key));
56 }
57
58 json::ref::ref(const ref & base, int index)
59 : m_js(base.m_js), m_path(base.m_path)
60 {
61 jassert(0 <= index && index < 10000); // Limit: large arrays not supported
62 m_path.push_back(node_info(index));
63 }
64
65 json::ref::ref(const ref & base, const char * /*dummy*/, const char * key_suffix)
66 : m_js(base.m_js), m_path(base.m_path)
67 {
68 int n = (int)m_path.size(), i;
69 for (i = n; --i >= 0; ) {
70 std::string & base_key = m_path[i].key;
71 if (base_key.empty())
72 continue; // skip array
73 base_key += key_suffix;
74 break;
75 }
76 jassert(i >= 0); // Limit: top level element must be an object
77 }
78
79 void json::ref::operator=(bool value)
80 {
81 m_js.set_bool(m_path, value);
82 }
83
84 void json::ref::operator=(long long value)
85 {
86 m_js.set_int64(m_path, (int64_t)value);
87 }
88
89 void json::ref::operator=(unsigned long long value)
90 {
91 m_js.set_uint64(m_path, (uint64_t)value);
92 }
93
94 void json::ref::operator=(int value)
95 {
96 operator=((long long)value);
97 }
98
99 void json::ref::operator=(unsigned value)
100 {
101 operator=((unsigned long long)value);
102 }
103
104 void json::ref::operator=(long value)
105 {
106 operator=((long long)value);
107 }
108
109 void json::ref::operator=(unsigned long value)
110 {
111 operator=((unsigned long long)value);
112 }
113
114 void json::ref::operator=(const std::string & value)
115 {
116 m_js.set_string(m_path, value);
117 }
118
119 void json::ref::operator=(const char * value)
120 {
121 jassert(value); // Limit: null not supported
122 operator=(std::string(value));
123 }
124
125 void json::ref::set_uint128(uint64_t value_hi, uint64_t value_lo)
126 {
127 if (!value_hi)
128 operator=((unsigned long long)value_lo);
129 else
130 m_js.set_uint128(m_path, value_hi, value_lo);
131 }
132
133 bool json::ref::set_if_safe_uint64(uint64_t value)
134 {
135 if (!is_safe_uint(value))
136 return false;
137 operator=((unsigned long long)value);
138 return true;
139 }
140
141 bool json::ref::set_if_safe_uint128(uint64_t value_hi, uint64_t value_lo)
142 {
143 if (value_hi)
144 return false;
145 return set_if_safe_uint64(value_lo);
146 }
147
148 bool json::ref::set_if_safe_le128(const void * pvalue)
149 {
150 return set_if_safe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue + 8),
151 sg_get_unaligned_le64( pvalue ));
152 }
153
154 void json::ref::set_unsafe_uint64(uint64_t value)
155 {
156 // Output as number "KEY"
157 operator=((unsigned long long)value);
158 if (!m_js.m_verbose && is_safe_uint(value))
159 return;
160 // Output as string "KEY_s"
161 char s[32];
162 snprintf(s, sizeof(s), "%" PRIu64, value);
163 with_suffix("_s") = s;
164 }
165
166 void json::ref::set_unsafe_uint128(uint64_t value_hi, uint64_t value_lo)
167 {
168 if (!m_js.m_verbose && !value_hi)
169 set_unsafe_uint64(value_lo);
170 else {
171 // Output as number "KEY", string "KEY_s" and LE byte array "KEY_le[]"
172 m_js.m_uint128_output = true;
173 set_uint128(value_hi, value_lo);
174 char s[64];
175 with_suffix("_s") = uint128_hilo_to_str(s, value_hi, value_lo);
176 ref le = with_suffix("_le");
177 for (int i = 0; i < 8; i++) {
178 uint64_t v = (value_lo >> (i << 3));
179 if (!v && !value_hi)
180 break;
181 le[i] = v & 0xff;
182 }
183 for (int i = 0; i < 8; i++) {
184 uint64_t v = value_hi >> (i << 3);
185 if (!v)
186 break;
187 le[8 + i] = v & 0xff;
188 }
189 }
190 }
191
192 void json::ref::set_unsafe_le128(const void * pvalue)
193 {
194 set_unsafe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue + 8),
195 sg_get_unaligned_le64( pvalue ));
196 }
197
198 json::node::node()
199 : type(nt_unset),
200 intval(0),
201 intval_hi(0)
202 {
203 }
204
205 json::node::node(const std::string & key_)
206 : type(nt_unset),
207 intval(0),
208 intval_hi(0),
209 key(key_)
210 {
211 }
212
213 json::node::~node()
214 {
215 for (size_t i = 0; i < childs.size(); i++)
216 delete childs[i];
217 }
218
219 json::node::const_iterator::const_iterator(const json::node * node_p, bool sorted)
220 : m_node_p(node_p),
221 m_use_map(sorted && node_p->type == nt_object),
222 m_child_idx(0)
223 {
224 if (m_use_map)
225 m_key_iter = node_p->key2index.begin();
226 }
227
228 bool json::node::const_iterator::at_end() const
229 {
230 if (m_use_map)
231 return (m_key_iter == m_node_p->key2index.end());
232 else
233 return (m_child_idx >= m_node_p->childs.size());
234 }
235
236 unsigned json::node::const_iterator::array_index() const
237 {
238 jassert(m_node_p->type == nt_array);
239 return m_child_idx;
240 }
241
242 void json::node::const_iterator::operator++()
243 {
244 if (m_use_map)
245 ++m_key_iter;
246 else
247 ++m_child_idx;
248 }
249
250 const json::node * json::node::const_iterator::operator*() const
251 {
252 if (m_use_map)
253 return m_node_p->childs[m_key_iter->second];
254 else
255 return m_node_p->childs[m_child_idx];
256 }
257
258 json::node * json::find_or_create_node(const json::node_path & path, node_type type)
259 {
260 node * p = &m_root_node;
261 for (unsigned i = 0; i < path.size(); i++) {
262 const node_info & pi = path[i];
263 if (!pi.key.empty()) {
264 // Object
265 if (p->type == nt_unset)
266 p->type = nt_object;
267 else
268 jassert(p->type == nt_object); // Limit: type change not supported
269 // Existing or new object element?
270 node::keymap::iterator ni = p->key2index.find(pi.key);
271 node * p2;
272 if (ni != p->key2index.end()) {
273 // Object element exists
274 p2 = p->childs[ni->second];
275 }
276 else {
277 // Create new object element
278 p->key2index[pi.key] = (unsigned)p->childs.size();
279 p2 = new node(pi.key);
280 p->childs.push_back(p2);
281 }
282 jassert(p2 && p2->key == pi.key);
283 p = p2;
284 }
285
286 else {
287 // Array
288 if (p->type == nt_unset)
289 p->type = nt_array;
290 else
291 jassert(p->type == nt_array); // Limit: type change not supported
292 node * p2;
293 // Existing or new array element?
294 if (pi.index < (int)p->childs.size()) {
295 // Array index exists
296 p2 = p->childs[pi.index];
297 if (!p2) // Already created ?
298 p->childs[pi.index] = p2 = new node;
299 }
300 else {
301 // Grow array, fill gap, create new element
302 p->childs.resize(pi.index + 1);
303 p->childs[pi.index] = p2 = new node;
304 }
305 jassert(p2 && p2->key.empty());
306 p = p2;
307 }
308 }
309
310 if ( p->type == nt_unset
311 || ( nt_int <= p->type && p->type <= nt_uint128
312 && nt_int <= type && type <= nt_uint128))
313 p->type = type;
314 else
315 jassert(p->type == type); // Limit: type change not supported
316 return p;
317 }
318
319 json::json()
320 : m_enabled(false),
321 m_verbose(false),
322 m_uint128_output(false)
323 {
324 }
325
326 void json::set_bool(const node_path & path, bool value)
327 {
328 if (!m_enabled)
329 return;
330 find_or_create_node(path, nt_bool)->intval = (value ? 1 : 0);
331 }
332
333 void json::set_int64(const node_path & path, int64_t value)
334 {
335 if (!m_enabled)
336 return;
337 find_or_create_node(path, nt_int)->intval = (uint64_t)value;
338 }
339
340 void json::set_uint64(const node_path & path, uint64_t value)
341 {
342 if (!m_enabled)
343 return;
344 find_or_create_node(path, nt_uint)->intval = value;
345 }
346
347 void json::set_uint128(const node_path & path, uint64_t value_hi, uint64_t value_lo)
348 {
349 if (!m_enabled)
350 return;
351 node * p = find_or_create_node(path, nt_uint128);
352 p->intval_hi = value_hi;
353 p->intval = value_lo;
354 }
355
356 void json::set_string(const node_path & path, const std::string & value)
357 {
358 if (!m_enabled)
359 return;
360 find_or_create_node(path, nt_string)->strval = value;
361 }
362
363 static void print_string(FILE * f, const char * s)
364 {
365 putc('"', f);
366 for (int i = 0; s[i]; i++) {
367 char c = s[i];
368 if (c == '"' || c == '\\')
369 putc('\\', f);
370 else if (c == '\t') {
371 putc('\\', f); c = 't';
372 }
373 else if ((unsigned char)c < ' ')
374 c = '?'; // Not ' '-'~', '\t' or UTF-8
375 putc(c, f);
376 }
377 putc('"', f);
378 }
379
380 void json::print_json(FILE * f, bool pretty, bool sorted, const node * p, int level)
381 {
382 if (!p->key.empty())
383 fprintf(f, "\"%s\":%s", p->key.c_str(), (pretty ? " " : ""));
384
385 switch (p->type) {
386 case nt_object:
387 case nt_array:
388 putc((p->type == nt_object ? '{' : '['), f);
389 if (!p->childs.empty()) {
390 bool first = true;
391 for (node::const_iterator it(p, sorted); !it.at_end(); ++it) {
392 if (!first)
393 putc(',', f);
394 if (pretty)
395 fprintf(f, "\n%*s", (level + 1) * 2, "");
396 const node * p2 = *it;
397 if (!p2) {
398 // Unset element of sparse array
399 jassert(p->type == nt_array);
400 fputs("null", f);
401 }
402 else {
403 // Recurse
404 print_json(f, pretty, sorted, p2, level + 1);
405 }
406 first = false;
407 }
408 if (pretty)
409 fprintf(f, "\n%*s", level * 2, "");
410 }
411 putc((p->type == nt_object ? '}' : ']'), f);
412 break;
413
414 case nt_bool:
415 fputs((p->intval ? "true" : "false"), f);
416 break;
417
418 case nt_int:
419 fprintf(f, "%" PRId64, (int64_t)p->intval);
420 break;
421
422 case nt_uint:
423 fprintf(f, "%" PRIu64, p->intval);
424 break;
425
426 case nt_uint128:
427 {
428 char buf[64];
429 fputs(uint128_hilo_to_str(buf, p->intval_hi, p->intval), f);
430 }
431 break;
432
433 case nt_string:
434 print_string(f, p->strval.c_str());
435 break;
436
437 default: jassert(false);
438 }
439 }
440
441 void json::print_flat(FILE * f, bool sorted, const node * p, std::string & path)
442 {
443 switch (p->type) {
444 case nt_object:
445 case nt_array:
446 fprintf(f, "%s = %s;\n", path.c_str(), (p->type == nt_object ? "{}" : "[]"));
447 if (!p->childs.empty()) {
448 unsigned len = path.size();
449 for (node::const_iterator it(p, sorted); !it.at_end(); ++it) {
450 const node * p2 = *it;
451 if (p->type == nt_array) {
452 char buf[10]; snprintf(buf, sizeof(buf), "[%u]", it.array_index());
453 path += buf;
454 }
455 else {
456 path += '.'; path += p2->key;
457 }
458 if (!p2) {
459 // Unset element of sparse array
460 jassert(p->type == nt_array);
461 fprintf(f, "%s = null;\n", path.c_str());
462 }
463 else {
464 // Recurse
465 print_flat(f, sorted, p2, path);
466 }
467 path.erase(len);
468 }
469 }
470 break;
471
472 case nt_bool:
473 fprintf(f, "%s = %s;\n", path.c_str(), (p->intval ? "true" : "false"));
474 break;
475
476 case nt_int:
477 fprintf(f, "%s = %" PRId64 ";\n", path.c_str(), (int64_t)p->intval);
478 break;
479
480 case nt_uint:
481 fprintf(f, "%s = %" PRIu64 ";\n", path.c_str(), p->intval);
482 break;
483
484 case nt_uint128:
485 {
486 char buf[64];
487 fprintf(f, "%s = %s;\n", path.c_str(),
488 uint128_hilo_to_str(buf, p->intval_hi, p->intval));
489 }
490 break;
491
492 case nt_string:
493 fprintf(f, "%s = ", path.c_str());
494 print_string(f, p->strval.c_str());
495 fputs(";\n", f);
496 break;
497
498 default: jassert(false);
499 }
500 }
501
502 void json::print(FILE * f, const print_options & options) const
503 {
504 if (m_root_node.type == nt_unset)
505 return;
506 jassert(m_root_node.type == nt_object);
507
508 if (!options.flat) {
509 print_json(f, options.pretty, options.sorted, &m_root_node, 0);
510 if (options.pretty)
511 putc('\n', f);
512 }
513 else {
514 std::string path("json");
515 print_flat(f, options.sorted, &m_root_node, path);
516 }
517 }