/src/oneDNN/src/graph/utils/json.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | /******************************************************************************* |
2 | | * Copyright 2020-2025 Intel Corporation |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | *******************************************************************************/ |
16 | | |
17 | | #ifndef GRAPH_UTILS_JSON_HPP |
18 | | #define GRAPH_UTILS_JSON_HPP |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cctype> |
22 | | #include <fstream> |
23 | | #include <iostream> |
24 | | #include <list> |
25 | | #include <map> |
26 | | #include <memory> |
27 | | #include <sstream> |
28 | | #include <string> |
29 | | #include <utility> |
30 | | #include <vector> |
31 | | #include <unordered_map> |
32 | | |
33 | | namespace dnnl { |
34 | | namespace impl { |
35 | | namespace graph { |
36 | | namespace utils { |
37 | | namespace json { |
38 | | |
39 | | /*! |
40 | | * \brief template to select type based on condition |
41 | | * For example, if_else_type<true, int, float>::Type will give int |
42 | | * \tparam cond the condition |
43 | | * \tparam Then the typename to be returned if cond is true |
44 | | * \tparam Else typename to be returned if cond is false |
45 | | */ |
46 | | |
47 | | template <bool cond, typename Then, typename Else> |
48 | | struct if_else_type; |
49 | | |
50 | | /*! |
51 | | * \brief generic serialization json |
52 | | * \tparam T the type to be serialized |
53 | | */ |
54 | | template <typename T> |
55 | | struct json_handler_t; |
56 | | |
57 | | template <typename T> |
58 | | struct common_json_t; |
59 | | |
60 | | /*! |
61 | | * \brief json to write any type. |
62 | | */ |
63 | | class json_writer_t { |
64 | | public: |
65 | 0 | json_writer_t(std::ostream *os) : os_(os) {} |
66 | | /*! |
67 | | * \brief object begin |
68 | | * \param multi_line whether to start an multi_line array. |
69 | | */ |
70 | | inline void begin_object(); |
71 | | /*! \brief object end. */ |
72 | | inline void end_object(); |
73 | | /*! |
74 | | * \brief write key value pair in the object. |
75 | | */ |
76 | | template <typename valuetype> |
77 | | inline void write_keyvalue(const std::string &key, const valuetype &value); |
78 | | /*! |
79 | | * \brief write a number. |
80 | | */ |
81 | | template <typename valuetype> |
82 | | inline void write_number(const valuetype &v); |
83 | | /*! |
84 | | * \brief write a string. |
85 | | */ |
86 | | inline void write_string(const std::string &s); |
87 | | /*! |
88 | | * \brief array begin. |
89 | | * \param multi_line if true, write multi_line. |
90 | | */ |
91 | | inline void begin_array(bool multi_line = true); |
92 | | /*! \brief array end. */ |
93 | | inline void end_array(); |
94 | | /*! |
95 | | * \brief write array separator. |
96 | | */ |
97 | | inline void write_array_seperator(); |
98 | | /*! |
99 | | * \brief write array value. |
100 | | */ |
101 | | template <typename valuetype> |
102 | | inline void write_array_item(const valuetype &value); |
103 | | |
104 | | inline void write_newline(); |
105 | | |
106 | | private: |
107 | | std::ostream *os_; |
108 | | /*! |
109 | | * \brief record how many element in the current scope. |
110 | | */ |
111 | | std::vector<size_t> scope_count_; |
112 | | /*! \brief record if current pos is a multiline scope */ |
113 | | std::vector<bool> scope_multi_line_; |
114 | | /*! |
115 | | * \brief write seperator space and newlines |
116 | | */ |
117 | | inline void write_seperator(); |
118 | | }; |
119 | | |
120 | | class json_reader_t { |
121 | | public: |
122 | 323 | explicit json_reader_t(std::istream *is) : is_(is) {} |
123 | | /*! |
124 | | * \brief parse json string. |
125 | | */ |
126 | | inline void read_string(std::string *out_str); |
127 | | /*! |
128 | | * \brief read number. |
129 | | */ |
130 | | template <typename valuetype> |
131 | | inline void read_number(valuetype *out_value); |
132 | | /*! |
133 | | * \brief parse an object begin. |
134 | | */ |
135 | | inline void begin_object(); |
136 | | /*! |
137 | | * \brief parse an object end. |
138 | | */ |
139 | | inline void begin_array(); |
140 | | /*! |
141 | | * \brief read next object, if true, will read next object. |
142 | | */ |
143 | | inline bool next_object_item(std::string *out_key); |
144 | | /*! |
145 | | * \brief read next object, if true, will read next object. |
146 | | */ |
147 | | inline bool next_array_item(); |
148 | | /*! |
149 | | * \brief read next value. |
150 | | */ |
151 | | template <typename valuetype> |
152 | | inline void read(valuetype *out_value); |
153 | | |
154 | | private: |
155 | | std::istream *is_; |
156 | | /*! |
157 | | * \brief record element size in the current. |
158 | | */ |
159 | | std::vector<size_t> scope_count_; |
160 | | /*! |
161 | | * \brief read next nonspace char. |
162 | | */ |
163 | | inline int next_nonspace(); |
164 | | inline int peeknext_nonspace(); |
165 | | /*! |
166 | | * \brief get the next char from the input. |
167 | | */ |
168 | | inline int next_char(); |
169 | | inline int peeknext_char(); |
170 | | }; |
171 | | |
172 | | class read_helper_t { |
173 | | public: |
174 | | /*! |
175 | | * \brief declare field |
176 | | */ |
177 | | template <typename T> |
178 | | inline void declare_field(const std::string &key, T *addr) { |
179 | | //declare_fieldInternal(key, addr); |
180 | | if (map_.count(key) == 0) { |
181 | | entry_t e; |
182 | | e.func = reader_function<T>; |
183 | | e.addr = static_cast<void *>(addr); |
184 | | map_[key] = e; |
185 | | } |
186 | | } |
187 | | /*! |
188 | | * \brief read all fields according to declare. |
189 | | */ |
190 | | inline bool read_fields(json_reader_t *reader); |
191 | | |
192 | | private: |
193 | | /*! |
194 | | * \brief reader function to store T. |
195 | | */ |
196 | | template <typename T> |
197 | | inline static void reader_function(json_reader_t *reader, void *addr); |
198 | | /*! \brief callback type to reader function */ |
199 | | using readfunc = void (*)(json_reader_t *reader, void *addr); |
200 | | /*! \brief data entry */ |
201 | | struct entry_t { |
202 | | readfunc func; |
203 | | /*! \brief store the address data for reading json*/ |
204 | | void *addr; |
205 | | }; |
206 | | /*! \brief reader callback */ |
207 | | std::map<std::string, entry_t> map_; |
208 | | }; |
209 | | |
210 | | template <typename then, typename other> |
211 | | struct if_else_type<true, then, other> { |
212 | | using type = then; |
213 | | }; |
214 | | |
215 | | template <typename then, typename other> |
216 | | struct if_else_type<false, then, other> { |
217 | | using type = other; |
218 | | }; |
219 | | |
220 | | template <typename valuetype> |
221 | | struct num_json_t { |
222 | | inline static void write(json_writer_t *writer, const valuetype &value) { |
223 | | writer->write_number<valuetype>(value); |
224 | | } |
225 | | inline static void read(json_reader_t *reader, valuetype *value) { |
226 | | reader->read_number<valuetype>(value); |
227 | | } |
228 | | }; |
229 | | |
230 | | template <typename valuetype> |
231 | | struct common_json_t { |
232 | | inline static void write(json_writer_t *writer, const valuetype &value) { |
233 | | value.save(writer); |
234 | | } |
235 | | inline static void read(json_reader_t *reader, valuetype *value) { |
236 | | value->load(reader); |
237 | | } |
238 | | }; |
239 | | |
240 | | template <typename valuetype> |
241 | | struct common_json_t<std::shared_ptr<valuetype>> { |
242 | | inline static void write( |
243 | | json_writer_t *writer, const std::shared_ptr<valuetype> &value) { |
244 | | auto *v = value.get(); |
245 | | v->save(writer); |
246 | | } |
247 | | |
248 | | inline static void read( |
249 | | json_reader_t *reader, std::shared_ptr<valuetype> *value) { |
250 | | auto ptr = std::make_shared<valuetype>(); |
251 | | auto *v = ptr.get(); |
252 | | v->load(reader); |
253 | | *value = std::move(ptr); |
254 | | } |
255 | | }; |
256 | | |
257 | | template <typename CT> |
258 | | struct array_json_t { |
259 | | inline static void write(json_writer_t *writer, const CT &array) { |
260 | | writer->begin_array(); |
261 | | for (typename CT::const_iterator it = array.begin(); it != array.end(); |
262 | | ++it) { |
263 | | writer->write_array_item(*it); |
264 | | } |
265 | | writer->end_array(); |
266 | | } |
267 | | inline static void read(json_reader_t *reader, CT *array) { |
268 | | using elemtype = typename CT::value_type; |
269 | | array->clear(); |
270 | | reader->begin_array(); |
271 | | while (reader->next_array_item()) { |
272 | | elemtype value; |
273 | | json_handler_t<elemtype>::read(reader, &value); |
274 | | array->insert(array->end(), value); |
275 | | } |
276 | | } |
277 | | }; |
278 | | |
279 | | template <typename CT> |
280 | | struct map_json_t { |
281 | | inline static void write(json_writer_t *writer, const CT &map) { |
282 | | writer->begin_object(); |
283 | | for (typename CT::const_iterator it = map.begin(); it != map.end(); |
284 | | ++it) { |
285 | | writer->write_keyvalue(it->first, it->second); |
286 | | } |
287 | | writer->end_object(); |
288 | | } |
289 | | inline static void read(json_reader_t *reader, CT *map) { |
290 | | using elemtype = typename CT::mapped_type; |
291 | | map->clear(); |
292 | | reader->begin_object(); |
293 | | std::string key; |
294 | | while (reader->next_object_item(&key)) { |
295 | | elemtype value; |
296 | | reader->read(&value); |
297 | | (*map)[key] = std::move(value); |
298 | | } |
299 | | } |
300 | | }; |
301 | | |
302 | | template <> |
303 | | struct json_handler_t<std::string> { |
304 | 0 | inline static void write(json_writer_t *writer, const std::string &value) { |
305 | 0 | writer->write_string(value); |
306 | 0 | } |
307 | 0 | inline static void read(json_reader_t *reader, std::string *str) { |
308 | 0 | reader->read_string(str); |
309 | 0 | } |
310 | | }; |
311 | | |
312 | | template <typename T> |
313 | | struct json_handler_t<std::map<std::string, T>> |
314 | | : public map_json_t<std::map<std::string, T>> {}; |
315 | | |
316 | | template <typename T> |
317 | | struct json_handler_t<std::unordered_map<std::string, T>> |
318 | | : public map_json_t<std::unordered_map<std::string, T>> {}; |
319 | | |
320 | | template <typename T> |
321 | | struct json_handler_t<std::vector<T>> : public array_json_t<std::vector<T>> {}; |
322 | | |
323 | | template <typename T> |
324 | | struct json_handler_t<std::list<T>> : public array_json_t<std::list<T>> {}; |
325 | | /*! |
326 | | * \brief generic serialization json |
327 | | */ |
328 | | template <typename T> |
329 | | struct json_handler_t { |
330 | | inline static void write(json_writer_t *writer, const T &data) { |
331 | | using Tjson = typename if_else_type<std::is_arithmetic<T>::value, |
332 | | num_json_t<T>, common_json_t<T>>::type; |
333 | | Tjson::write(writer, data); |
334 | | } |
335 | | inline static void read(json_reader_t *reader, T *data) { |
336 | | using Tjson = typename if_else_type<std::is_arithmetic<T>::value, |
337 | | num_json_t<T>, common_json_t<T>>::type; |
338 | | Tjson::read(reader, data); |
339 | | } |
340 | | }; |
341 | | |
342 | 0 | inline void json_writer_t::begin_object() { |
343 | 0 | *os_ << "{"; |
344 | 0 | scope_multi_line_.push_back(true); |
345 | 0 | scope_count_.push_back(0); |
346 | 0 | } |
347 | | |
348 | | template <typename valuetype> |
349 | | inline void json_writer_t::write_keyvalue( |
350 | | const std::string &key, const valuetype &value) { |
351 | | if (scope_count_.back() > 0) { *os_ << ","; } |
352 | | write_seperator(); |
353 | | *os_ << '\"'; |
354 | | *os_ << key; |
355 | | *os_ << "\": "; |
356 | | scope_count_.back() += 1; |
357 | | json_handler_t<valuetype>::write(this, value); |
358 | | } |
359 | | |
360 | | template <typename valuetype> |
361 | | inline void json_writer_t::write_number(const valuetype &v) { |
362 | | *os_ << v; |
363 | | } |
364 | | |
365 | 0 | inline void json_writer_t::write_string(const std::string &s) { |
366 | 0 | *os_ << '\"'; |
367 | 0 | for (size_t i = 0; i < s.length(); ++i) { |
368 | 0 | char ch = s[i]; |
369 | 0 | switch (ch) { |
370 | 0 | case '\r': *os_ << "\\r"; break; |
371 | 0 | case '\n': *os_ << "\\n"; break; |
372 | 0 | case '\\': *os_ << "\\\\"; break; |
373 | 0 | case '\t': *os_ << "\\t"; break; |
374 | 0 | case '\"': *os_ << "\\\""; break; |
375 | 0 | default: *os_ << ch; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | *os_ << '\"'; |
379 | 0 | } |
380 | | |
381 | 0 | inline void json_writer_t::begin_array(bool multi_line) { |
382 | 0 | *os_ << '['; |
383 | 0 | scope_multi_line_.push_back(multi_line); |
384 | 0 | scope_count_.push_back(0); |
385 | 0 | } |
386 | | |
387 | 0 | inline void json_writer_t::end_array() { |
388 | 0 | if (!scope_count_.empty() && !scope_multi_line_.empty()) { |
389 | 0 | bool newline = scope_multi_line_.back(); |
390 | 0 | size_t nelem = scope_count_.back(); |
391 | 0 | scope_multi_line_.pop_back(); |
392 | 0 | scope_count_.pop_back(); |
393 | 0 | if (newline && nelem != 0) write_seperator(); |
394 | 0 | } |
395 | 0 | *os_ << ']'; |
396 | 0 | } |
397 | | |
398 | 0 | inline void json_writer_t::write_array_seperator() { |
399 | 0 | if (scope_count_.back() != 0) { *os_ << ","; } |
400 | 0 | scope_count_.back() += 1; |
401 | 0 | write_seperator(); |
402 | 0 | } |
403 | | |
404 | | template <typename valuetype> |
405 | | inline void json_writer_t::write_array_item(const valuetype &value) { |
406 | | this->write_array_seperator(); |
407 | | json::json_handler_t<valuetype>::write(this, value); |
408 | | } |
409 | | |
410 | 0 | inline void json_writer_t::end_object() { |
411 | 0 | if (!scope_count_.empty() && !scope_multi_line_.empty()) { |
412 | 0 | bool newline = scope_multi_line_.back(); |
413 | 0 | size_t nelem = scope_count_.back(); |
414 | 0 | scope_multi_line_.pop_back(); |
415 | 0 | scope_count_.pop_back(); |
416 | 0 | if (newline && nelem != 0) write_seperator(); |
417 | 0 | } |
418 | 0 | *os_ << '}'; |
419 | 0 | } |
420 | | |
421 | 0 | inline void json_writer_t::write_seperator() { |
422 | 0 | if (scope_multi_line_.empty() || scope_multi_line_.back()) { |
423 | 0 | *os_ << '\n'; |
424 | 0 | *os_ << std::string(scope_multi_line_.size() * 2, ' '); |
425 | 0 | } |
426 | 0 | } |
427 | | |
428 | 0 | inline void json_writer_t::write_newline() { |
429 | 0 | *os_ << '\n'; |
430 | 0 | } |
431 | | |
432 | 1.94M | inline int json_reader_t::next_char() { |
433 | 1.94M | return is_->get(); |
434 | 1.94M | } |
435 | | |
436 | 490 | inline int json_reader_t::peeknext_char() { |
437 | 490 | return is_->peek(); |
438 | 490 | } |
439 | | |
440 | 1.58M | inline int json_reader_t::next_nonspace() { |
441 | 1.58M | int ch; |
442 | 1.58M | do { |
443 | 1.58M | ch = next_char(); |
444 | 1.58M | } while (isspace(ch)); |
445 | 1.58M | return ch; |
446 | 1.58M | } |
447 | | |
448 | 287 | inline int json_reader_t::peeknext_nonspace() { |
449 | 287 | int ch; |
450 | 490 | while (true) { |
451 | 490 | ch = peeknext_char(); |
452 | 490 | if (!isspace(ch)) break; |
453 | 203 | next_char(); |
454 | 203 | } |
455 | 287 | return ch; |
456 | 287 | } |
457 | | |
458 | 527k | inline void json_reader_t::read_string(std::string *out_str) { |
459 | 527k | int ch = next_nonspace(); |
460 | 527k | if (ch == '\"') { |
461 | 2.06k | std::ostringstream output; |
462 | 359k | while (true) { |
463 | 359k | ch = next_char(); |
464 | 359k | if (ch == '\\') { |
465 | 4.33k | char sch = static_cast<char>(next_char()); |
466 | 4.33k | switch (sch) { |
467 | 1.14k | case 'r': output << "\r"; break; |
468 | 546 | case 'n': output << "\r"; break; |
469 | 980 | case '\\': output << "\r"; break; |
470 | 1.17k | case 't': output << "\r"; break; |
471 | 481 | case '\"': output << "\r"; break; |
472 | 6 | default: throw("unknown string escape."); |
473 | 4.33k | } |
474 | 355k | } else { |
475 | 355k | if (ch == '\"') break; |
476 | 353k | output << static_cast<char>(ch); |
477 | 353k | } |
478 | 357k | if (ch == EOF || ch == '\r' || ch == '\n') { |
479 | 109 | throw("error at!"); |
480 | 0 | return; |
481 | 109 | } |
482 | 357k | } |
483 | 1.95k | *out_str = output.str(); |
484 | 1.95k | } |
485 | 527k | } |
486 | | |
487 | | template <typename valuetype> |
488 | | inline void json_reader_t::read_number(valuetype *out_value) { |
489 | | *is_ >> *out_value; |
490 | | } |
491 | | |
492 | 323 | inline void json_reader_t::begin_object() { |
493 | 323 | int ch = next_nonspace(); |
494 | 323 | if (ch == '{') { scope_count_.push_back(0); } |
495 | 323 | } |
496 | | |
497 | 0 | inline void json_reader_t::begin_array() { |
498 | 0 | int ch = next_nonspace(); |
499 | 0 | if (ch == '[') { scope_count_.push_back(0); } |
500 | 0 | } |
501 | | |
502 | 527k | inline bool json_reader_t::next_object_item(std::string *out_key) { |
503 | 527k | bool next = true; |
504 | 527k | if (scope_count_.empty()) { return false; } |
505 | 527k | if (scope_count_.back() != 0) { |
506 | 526k | int ch = next_nonspace(); |
507 | 526k | if (ch == EOF) { |
508 | 29 | next = false; |
509 | 526k | } else if (ch == '}') { |
510 | 1 | next = false; |
511 | 526k | } else { |
512 | 526k | if (ch != ',') { return false; } |
513 | 526k | } |
514 | 526k | } else { |
515 | 287 | int ch = peeknext_nonspace(); |
516 | 287 | if (ch == '}') { |
517 | 1 | next_char(); |
518 | 1 | next = false; |
519 | 1 | } |
520 | 287 | } |
521 | 527k | if (!next) { |
522 | 31 | scope_count_.pop_back(); |
523 | 31 | return false; |
524 | 527k | } else { |
525 | 527k | scope_count_.back() += 1; |
526 | 527k | read_string(out_key); |
527 | 527k | int ch = next_nonspace(); |
528 | 527k | return (ch == ':'); |
529 | 527k | } |
530 | 527k | } |
531 | | |
532 | 0 | inline bool json_reader_t::next_array_item() { |
533 | 0 | bool next = true; |
534 | 0 | if (scope_count_.empty()) { return false; } |
535 | 0 | if (scope_count_.back() != 0) { |
536 | 0 | int ch = next_nonspace(); |
537 | 0 | if (ch == EOF) { |
538 | 0 | next = false; |
539 | 0 | } else if (ch == ']') { |
540 | 0 | next = false; |
541 | 0 | } else { |
542 | 0 | if (ch != ',') { return false; } |
543 | 0 | } |
544 | 0 | } else { |
545 | 0 | int ch = peeknext_nonspace(); |
546 | 0 | if (ch == ']') { |
547 | 0 | next_char(); |
548 | 0 | next = false; |
549 | 0 | } |
550 | 0 | } |
551 | 0 | if (!next) { |
552 | 0 | scope_count_.pop_back(); |
553 | 0 | return false; |
554 | 0 | } else { |
555 | 0 | scope_count_.back() += 1; |
556 | 0 | return true; |
557 | 0 | } |
558 | 0 | } |
559 | | |
560 | | template <typename valuetype> |
561 | | inline void json_reader_t::read(valuetype *out_value) { |
562 | | json::json_handler_t<valuetype>::read(this, out_value); |
563 | | } |
564 | | |
565 | 323 | inline bool read_helper_t::read_fields(json_reader_t *reader) { |
566 | 323 | reader->begin_object(); |
567 | 323 | std::map<std::string, int> visited; |
568 | 323 | std::string key; |
569 | 527k | while (reader->next_object_item(&key)) { |
570 | 526k | if (map_.count(key) != 0) { |
571 | 0 | entry_t e = map_[key]; |
572 | 0 | (*e.func)(reader, e.addr); |
573 | 0 | visited[key] = 0; |
574 | 0 | } |
575 | 526k | } |
576 | 323 | if (visited.size() != map_.size()) { |
577 | 0 | for (std::map<std::string, entry_t>::iterator it = map_.begin(); |
578 | 0 | it != map_.end(); ++it) { |
579 | 0 | if (visited.count(it->first) != 1) { return false; } |
580 | 0 | } |
581 | 0 | } |
582 | 323 | return true; |
583 | 323 | } |
584 | | |
585 | | template <typename T> |
586 | | inline void read_helper_t::reader_function(json_reader_t *reader, void *addr) { |
587 | | json::json_handler_t<T>::read(reader, static_cast<T *>(addr)); |
588 | | } |
589 | | |
590 | | } // namespace json |
591 | | } // namespace utils |
592 | | } // namespace graph |
593 | | } // namespace impl |
594 | | } // namespace dnnl |
595 | | |
596 | | #endif |