1 | // SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later |
2 | // Copyright 2011, SIL International, All rights reserved. |
3 | |
4 | // JSON debug logging |
5 | // Author: Tim Eves |
6 | |
7 | #if !defined GRAPHITE2_NTRACING |
8 | |
9 | #include <cstdio> |
10 | #include <limits> |
11 | #include "inc/json.h" |
12 | |
13 | #if defined(_MSC_VER) |
14 | #define FORMAT_INTMAX "%lli" |
15 | #define FORMAT_UINTMAX "%llu" |
16 | #else |
17 | #define FORMAT_INTMAX "%ji" |
18 | #define FORMAT_UINTMAX "%ju" |
19 | #endif |
20 | |
21 | using namespace graphite2; |
22 | |
23 | namespace |
24 | { |
25 | enum |
26 | { |
27 | seq = ',', |
28 | obj='}', member=':', empty_obj='{', |
29 | arr=']', empty_arr='[' |
30 | }; |
31 | } |
32 | |
33 | const std::nullptr_t json::null = nullptr; |
34 | |
35 | inline |
36 | void json::context(const char current) throw() |
37 | { |
38 | fprintf(_stream, "%c" , *_context); |
39 | indent(); |
40 | *_context = current; |
41 | } |
42 | |
43 | |
44 | void json::indent(const int d) throw() |
45 | { |
46 | if (*_context == member || (_flatten && _flatten < _context)) |
47 | fputc(' ', _stream); |
48 | else |
49 | fprintf(_stream, "\n%*s" , 4*int(_context - _contexts + d), "" ); |
50 | } |
51 | |
52 | |
53 | inline |
54 | void json::push_context(const char prefix, const char suffix) throw() |
55 | { |
56 | assert(_context - _contexts < ptrdiff_t(sizeof _contexts)); |
57 | |
58 | if (_context == _contexts) |
59 | *_context = suffix; |
60 | else |
61 | context(suffix); |
62 | *++_context = prefix; |
63 | } |
64 | |
65 | |
66 | void json::pop_context() throw() |
67 | { |
68 | assert(_context > _contexts); |
69 | |
70 | if (*_context == seq) indent(-1); |
71 | else fputc(*_context, _stream); |
72 | |
73 | fputc(*--_context, _stream); |
74 | if (_context == _contexts) fputc('\n', _stream); |
75 | fflush(_stream); |
76 | |
77 | if (_flatten >= _context) _flatten = 0; |
78 | *_context = seq; |
79 | } |
80 | |
81 | |
82 | // These four functions cannot be inlined as pointers to these |
83 | // functions are needed for operator << (_context_t) to work. |
84 | void json::flat(json & j) throw() { if (!j._flatten) j._flatten = j._context; } |
85 | void json::close(json & j) throw() { j.pop_context(); } |
86 | void json::object(json & j) throw() { j.push_context('{', '}'); } |
87 | void json::array(json & j) throw() { j.push_context('[', ']'); } |
88 | void json::item(json & j) throw() |
89 | { |
90 | while (j._context > j._contexts+1 && j._context[-1] != arr) |
91 | j.pop_context(); |
92 | } |
93 | |
94 | |
95 | json & json::operator << (json::string s) throw() |
96 | { |
97 | const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq; |
98 | context(ctxt); |
99 | fprintf(_stream, "\"%s\"" , s); |
100 | if (ctxt == member) fputc(' ', _stream); |
101 | |
102 | return *this; |
103 | } |
104 | |
105 | json & json::operator << (json::number f) throw() |
106 | { |
107 | context(seq); |
108 | if (std::numeric_limits<json::number>::infinity() == f) |
109 | fputs("Infinity" , _stream); |
110 | else if (-std::numeric_limits<json::number>::infinity() == f) |
111 | fputs("-Infinity" , _stream); |
112 | else if (std::numeric_limits<json::number>::quiet_NaN() == f || |
113 | std::numeric_limits<json::number>::signaling_NaN() == f) |
114 | fputs("NaN" , _stream); |
115 | else |
116 | fprintf(_stream, "%g" , f); |
117 | return *this; |
118 | } |
119 | json & json::operator << (json::integer d) throw() { context(seq); fprintf(_stream, FORMAT_INTMAX, intmax_t(d)); return *this; } |
120 | json & json::operator << (json::integer_u d) throw() { context(seq); fprintf(_stream, FORMAT_UINTMAX, uintmax_t(d)); return *this; } |
121 | json & json::operator << (json::boolean b) throw() { context(seq); fputs(b ? "true" : "false" , _stream); return *this; } |
122 | json & json::operator << (std::nullptr_t) throw() { context(seq); fputs("null" ,_stream); return *this; } |
123 | |
124 | #endif |
125 | |