| 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 | |