/* Copyright: © 2011,2018 SIL International. Description: JSON pretty printer for dumping debug/diagnostic data structure. Create Date: 15 Dec 2011 Authors: Tim Eves (TSE) History: 28 Sep 2018 - TSE - Imported from graphite2 project. - TSE - Refactored to use C++ std::ostream. 25 Oct 2018 - TSE - Relicensed under the MIT license for inclusion in the Keyman project. */ #include #include #include #include "json.hpp" #if defined(_MSC_VER) #define FORMAT_INTMAX "%lli" #define FORMAT_UINTMAX "%llu" #else #define FORMAT_INTMAX "%ji" #define FORMAT_UINTMAX "%ju" #endif namespace { enum { seq = ',', obj='}', member=':', empty_obj='{', arr=']', empty_arr='[' }; } const std::nullptr_t json::null = nullptr; inline void json::context(const char current) throw() { _stream << *_context; indent(); *_context = current; } void json::indent(const int d) throw() { if (*_context == member || (_flatten && _flatten < _context)) _stream.put(' '); else _stream << std::endl << std::setw(4*int(_context - _contexts + d)) << ""; } inline void json::push_context(const char prefix, const char suffix) throw() { assert(_context - _contexts < ptrdiff_t(sizeof _contexts)); if (_context == _contexts) *_context = suffix; else context(suffix); *++_context = prefix; } void json::pop_context() throw() { assert(_context > _contexts); if (*_context == seq) indent(-1); else _stream.put(*_context); _stream.put(*--_context); if (_context == _contexts) _stream << std::endl; _stream.flush(); if (_flatten >= _context) _flatten = 0; *_context = seq; } // These four functions cannot be inlined as pointers to these // functions are needed for operator << (_context_t) to work. void json::flat(json & j) throw() { if (!j._flatten) j._flatten = j._context; } void json::close(json & j) throw() { j.pop_context(); } void json::object(json & j) throw() { j.push_context('{', '}'); } void json::array(json & j) throw() { j.push_context('[', ']'); } void json::item(json & j) throw() { while (j._context > j._contexts+1 && j._context[-1] != arr) j.pop_context(); } json & json::operator << (json::string s) throw() { const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq; context(ctxt); _stream << '"' << s << '"'; if (ctxt == member) _stream.put(' '); return *this; } json & json::operator << (json::number f) throw() { context(seq); if (std::numeric_limits::infinity() == f) _stream << "Infinity"; else if (-std::numeric_limits::infinity() == f) _stream << "-Infinity"; else if (std::numeric_limits::quiet_NaN() == f || std::numeric_limits::signaling_NaN() == f) _stream << "NaN"; else _stream << f; return *this; } json & json::operator << (json::integer d) throw() { context(seq); _stream << intmax_t(d); return *this; } json & json::operator << (json::integer_u d) throw(){ context(seq); _stream << uintmax_t(d); return *this; } json & json::operator << (json::boolean b) throw() { context(seq); _stream << (b ? "true" : "false"); return *this; } json & json::operator << (std::nullptr_t) throw() { context(seq); _stream << "null"; return *this; }