]>
git.proxmox.com Git - mirror_smartmontools-debian.git/blob - json.cpp
4 * Home page of code is: https://www.smartmontools.org
6 * Copyright (C) 2017-18 Christian Franke
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
16 const char * json_cvsid
= "$Id: json.cpp 4830 2018-11-02 21:21:48Z chrfranke $"
19 #include "sg_unaligned.h"
20 #include "utility.h" // uint128_*()
25 static void jassert_failed(int line
, const char * expr
)
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
);
33 #define jassert(expr) (!(expr) ? jassert_failed(__LINE__, #expr) : (void)0)
35 static void check_key(const char * key
)
37 // Limit: object keys should be valid identifiers (lowercase only)
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
== '_'));
44 json::ref::ref(json
& js
, const char * key
)
48 m_path
.push_back(node_info(key
));
51 json::ref::ref(const ref
& base
, const char * key
)
52 : m_js(base
.m_js
), m_path(base
.m_path
)
55 m_path
.push_back(node_info(key
));
58 json::ref::ref(const ref
& base
, int index
)
59 : m_js(base
.m_js
), m_path(base
.m_path
)
61 jassert(0 <= index
&& index
< 10000); // Limit: large arrays not supported
62 m_path
.push_back(node_info(index
));
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
)
68 int n
= (int)m_path
.size(), i
;
69 for (i
= n
; --i
>= 0; ) {
70 std::string
& base_key
= m_path
[i
].key
;
72 continue; // skip array
73 base_key
+= key_suffix
;
76 jassert(i
>= 0); // Limit: top level element must be an object
79 void json::ref::operator=(bool value
)
81 m_js
.set_bool(m_path
, value
);
84 void json::ref::operator=(long long value
)
86 m_js
.set_int64(m_path
, (int64_t)value
);
89 void json::ref::operator=(unsigned long long value
)
91 m_js
.set_uint64(m_path
, (uint64_t)value
);
94 void json::ref::operator=(int value
)
96 operator=((long long)value
);
99 void json::ref::operator=(unsigned value
)
101 operator=((unsigned long long)value
);
104 void json::ref::operator=(long value
)
106 operator=((long long)value
);
109 void json::ref::operator=(unsigned long value
)
111 operator=((unsigned long long)value
);
114 void json::ref::operator=(const std::string
& value
)
116 m_js
.set_string(m_path
, value
);
119 void json::ref::operator=(const char * value
)
121 jassert(value
); // Limit: null not supported
122 operator=(std::string(value
));
125 void json::ref::set_uint128(uint64_t value_hi
, uint64_t value_lo
)
128 operator=((unsigned long long)value_lo
);
130 m_js
.set_uint128(m_path
, value_hi
, value_lo
);
133 bool json::ref::set_if_safe_uint64(uint64_t value
)
135 if (!is_safe_uint(value
))
137 operator=((unsigned long long)value
);
141 bool json::ref::set_if_safe_uint128(uint64_t value_hi
, uint64_t value_lo
)
145 return set_if_safe_uint64(value_lo
);
148 bool json::ref::set_if_safe_le128(const void * pvalue
)
150 return set_if_safe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue
+ 8),
151 sg_get_unaligned_le64( pvalue
));
154 void json::ref::set_unsafe_uint64(uint64_t value
)
156 // Output as number "KEY"
157 operator=((unsigned long long)value
);
158 if (!m_js
.m_verbose
&& is_safe_uint(value
))
160 // Output as string "KEY_s"
162 snprintf(s
, sizeof(s
), "%" PRIu64
, value
);
163 with_suffix("_s") = s
;
166 void json::ref::set_unsafe_uint128(uint64_t value_hi
, uint64_t value_lo
)
168 if (!m_js
.m_verbose
&& !value_hi
)
169 set_unsafe_uint64(value_lo
);
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
);
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));
183 for (int i
= 0; i
< 8; i
++) {
184 uint64_t v
= value_hi
>> (i
<< 3);
187 le
[8 + i
] = v
& 0xff;
192 void json::ref::set_unsafe_le128(const void * pvalue
)
194 set_unsafe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue
+ 8),
195 sg_get_unaligned_le64( pvalue
));
205 json::node::node(const std::string
& key_
)
215 for (size_t i
= 0; i
< childs
.size(); i
++)
219 json::node::const_iterator::const_iterator(const json::node
* node_p
, bool sorted
)
221 m_use_map(sorted
&& node_p
->type
== nt_object
),
225 m_key_iter
= node_p
->key2index
.begin();
228 bool json::node::const_iterator::at_end() const
231 return (m_key_iter
== m_node_p
->key2index
.end());
233 return (m_child_idx
>= m_node_p
->childs
.size());
236 unsigned json::node::const_iterator::array_index() const
238 jassert(m_node_p
->type
== nt_array
);
242 void json::node::const_iterator::operator++()
250 const json::node
* json::node::const_iterator::operator*() const
253 return m_node_p
->childs
[m_key_iter
->second
];
255 return m_node_p
->childs
[m_child_idx
];
258 json::node
* json::find_or_create_node(const json::node_path
& path
, node_type type
)
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()) {
265 if (p
->type
== nt_unset
)
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
);
272 if (ni
!= p
->key2index
.end()) {
273 // Object element exists
274 p2
= p
->childs
[ni
->second
];
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
);
282 jassert(p2
&& p2
->key
== pi
.key
);
288 if (p
->type
== nt_unset
)
291 jassert(p
->type
== nt_array
); // Limit: type change not supported
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
;
301 // Grow array, fill gap, create new element
302 p
->childs
.resize(pi
.index
+ 1);
303 p
->childs
[pi
.index
] = p2
= new node
;
305 jassert(p2
&& p2
->key
.empty());
310 if ( p
->type
== nt_unset
311 || ( nt_int
<= p
->type
&& p
->type
<= nt_uint128
312 && nt_int
<= type
&& type
<= nt_uint128
))
315 jassert(p
->type
== type
); // Limit: type change not supported
322 m_uint128_output(false)
326 void json::set_bool(const node_path
& path
, bool value
)
330 find_or_create_node(path
, nt_bool
)->intval
= (value
? 1 : 0);
333 void json::set_int64(const node_path
& path
, int64_t value
)
337 find_or_create_node(path
, nt_int
)->intval
= (uint64_t)value
;
340 void json::set_uint64(const node_path
& path
, uint64_t value
)
344 find_or_create_node(path
, nt_uint
)->intval
= value
;
347 void json::set_uint128(const node_path
& path
, uint64_t value_hi
, uint64_t value_lo
)
351 node
* p
= find_or_create_node(path
, nt_uint128
);
352 p
->intval_hi
= value_hi
;
353 p
->intval
= value_lo
;
356 void json::set_string(const node_path
& path
, const std::string
& value
)
360 find_or_create_node(path
, nt_string
)->strval
= value
;
363 static void print_string(FILE * f
, const char * s
)
366 for (int i
= 0; s
[i
]; i
++) {
368 if (c
== '"' || c
== '\\')
370 else if (c
== '\t') {
371 putc('\\', f
); c
= 't';
373 else if ((unsigned char)c
< ' ')
374 c
= '?'; // Not ' '-'~', '\t' or UTF-8
380 void json::print_json(FILE * f
, bool pretty
, bool sorted
, const node
* p
, int level
)
383 fprintf(f
, "\"%s\":%s", p
->key
.c_str(), (pretty
? " " : ""));
388 putc((p
->type
== nt_object
? '{' : '['), f
);
389 if (!p
->childs
.empty()) {
391 for (node::const_iterator
it(p
, sorted
); !it
.at_end(); ++it
) {
395 fprintf(f
, "\n%*s", (level
+ 1) * 2, "");
396 const node
* p2
= *it
;
398 // Unset element of sparse array
399 jassert(p
->type
== nt_array
);
404 print_json(f
, pretty
, sorted
, p2
, level
+ 1);
409 fprintf(f
, "\n%*s", level
* 2, "");
411 putc((p
->type
== nt_object
? '}' : ']'), f
);
415 fputs((p
->intval
? "true" : "false"), f
);
419 fprintf(f
, "%" PRId64
, (int64_t)p
->intval
);
423 fprintf(f
, "%" PRIu64
, p
->intval
);
429 fputs(uint128_hilo_to_str(buf
, p
->intval_hi
, p
->intval
), f
);
434 print_string(f
, p
->strval
.c_str());
437 default: jassert(false);
441 void json::print_flat(FILE * f
, bool sorted
, const node
* p
, std::string
& path
)
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());
456 path
+= '.'; path
+= p2
->key
;
459 // Unset element of sparse array
460 jassert(p
->type
== nt_array
);
461 fprintf(f
, "%s = null;\n", path
.c_str());
465 print_flat(f
, sorted
, p2
, path
);
473 fprintf(f
, "%s = %s;\n", path
.c_str(), (p
->intval
? "true" : "false"));
477 fprintf(f
, "%s = %" PRId64
";\n", path
.c_str(), (int64_t)p
->intval
);
481 fprintf(f
, "%s = %" PRIu64
";\n", path
.c_str(), p
->intval
);
487 fprintf(f
, "%s = %s;\n", path
.c_str(),
488 uint128_hilo_to_str(buf
, p
->intval_hi
, p
->intval
));
493 fprintf(f
, "%s = ", path
.c_str());
494 print_string(f
, p
->strval
.c_str());
498 default: jassert(false);
502 void json::print(FILE * f
, const print_options
& options
) const
504 if (m_root_node
.type
== nt_unset
)
506 jassert(m_root_node
.type
== nt_object
);
509 print_json(f
, options
.pretty
, options
.sorted
, &m_root_node
, 0);
514 std::string
path("json");
515 print_flat(f
, options
.sorted
, &m_root_node
, path
);