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
21using namespace graphite2;
22
23namespace
24{
25 enum
26 {
27 seq = ',',
28 obj='}', member=':', empty_obj='{',
29 arr=']', empty_arr='['
30 };
31}
32
33const std::nullptr_t json::null = nullptr;
34
35inline
36void json::context(const char current) throw()
37{
38 fprintf(_stream, "%c", *_context);
39 indent();
40 *_context = current;
41}
42
43
44void 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
53inline
54void 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
66void 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.
84void json::flat(json & j) throw() { if (!j._flatten) j._flatten = j._context; }
85void json::close(json & j) throw() { j.pop_context(); }
86void json::object(json & j) throw() { j.push_context('{', '}'); }
87void json::array(json & j) throw() { j.push_context('[', ']'); }
88void json::item(json & j) throw()
89{
90 while (j._context > j._contexts+1 && j._context[-1] != arr)
91 j.pop_context();
92}
93
94
95json & 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
105json & 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}
119json & json::operator << (json::integer d) throw() { context(seq); fprintf(_stream, FORMAT_INTMAX, intmax_t(d)); return *this; }
120json & json::operator << (json::integer_u d) throw() { context(seq); fprintf(_stream, FORMAT_UINTMAX, uintmax_t(d)); return *this; }
121json & json::operator << (json::boolean b) throw() { context(seq); fputs(b ? "true" : "false", _stream); return *this; }
122json & json::operator << (std::nullptr_t) throw() { context(seq); fputs("null",_stream); return *this; }
123
124#endif
125