#include "json_prettifier.h" #include #include #include #include #include namespace NJson { struct TRewritableOut { IOutputStream& Slave; char Last = 0; bool Dirty = false; TRewritableOut(IOutputStream& sl) : Slave(sl) { } template void Write(const T& t) { Flush(); Slave << t; } void Hold(char c) { if (Dirty) Flush(); Last = c; Dirty = true; } void Flush() { if (Dirty) { Slave << Last; Dirty = false; } } void Revert() { Dirty = false; } }; struct TSpaces { char S[256]; TSpaces() { memset(&S, ' ', sizeof(S)); } TStringBuf Get(ui8 sz) const { return TStringBuf(S, sz); } }; bool TJsonPrettifier::MayUnquoteNew(TStringBuf s) { static str_spn alpha("a-zA-Z_@$", true); static str_spn alnum("a-zA-Z_@$0-9.-", true); static TStringBuf true0("true"); static TStringBuf false0("false"); static TStringBuf null0("null"); return !!s && alpha.chars_table[(ui8)s[0]] && alnum.cbrk(s.begin() + 1, s.end()) == s.end() && !EqualToOneOf(s, null0, true0, false0); } // to keep arcadia tests happy bool TJsonPrettifier::MayUnquoteOld(TStringBuf s) { static str_spn alpha("a-zA-Z_@$", true); static str_spn alnum("a-zA-Z_@$0-9", true); static TStringBuf true0("true"); static TStringBuf false0("false"); static TStringBuf true1("on"); static TStringBuf false1("off"); static TStringBuf true2("da"); static TStringBuf false2("net"); static TStringBuf null0("null"); return !!s && alpha.chars_table[(ui8)s[0]] && alnum.cbrk(s.begin() + 1, s.end()) == s.end() && !EqualToOneOf(s, null0, true0, false0, true1, false1, true2, false2); } class TPrettifier: public TJsonCallbacks { TRewritableOut Out; TStringBuf Spaces; TStringBuf Quote; TStringBuf Unsafe; TStringBuf Safe; ui32 Level = 0; ui32 MaxPaddingLevel; bool Unquote = false; bool Compactify = false; bool NewUnquote = false; public: TPrettifier(IOutputStream& out, const TJsonPrettifier& p) : Out(out) , MaxPaddingLevel(p.MaxPaddingLevel) , Unquote(p.Unquote) , Compactify(p.Compactify) , NewUnquote(p.NewUnquote) { static TSpaces spaces; Spaces = spaces.Get(p.Padding); if (p.SingleQuotes) { Quote = Unsafe = "'"; Safe = "\""; } else { Quote = Unsafe = "\""; Safe = "'"; } } void Pad(bool close = false) { if (Compactify) { Out.Flush(); return; } if (Level > MaxPaddingLevel || (Level == MaxPaddingLevel && close)) { Out.Write(" "); return; } if (Level || close) { Out.Write(Spaces ? "\n" : " "); } for (ui32 i = 0; i < Level; ++i) { Out.Write(Spaces); } } void WriteSpace(char sp) { if (Compactify) { Out.Flush(); return; } Out.Write(sp); } void OnVal() { if (Out.Dirty && ':' == Out.Last) { WriteSpace(' '); } else { Pad(); } } void AfterVal() { Out.Hold(','); } template bool WriteVal(const T& t) { OnVal(); Out.Write(t); AfterVal(); return true; } bool OnNull() override { return WriteVal(TStringBuf("null")); } bool OnBoolean(bool v) override { return WriteVal(v ? TStringBuf("true") : TStringBuf("false")); } bool OnInteger(long long i) override { return WriteVal(i); } bool OnUInteger(unsigned long long i) override { return WriteVal(i); } bool OnDouble(double d) override { return WriteVal(d); } void WriteString(TStringBuf s) { if (Unquote && (NewUnquote ? TJsonPrettifier::MayUnquoteNew(s) : TJsonPrettifier::MayUnquoteOld(s))) { Out.Slave << s; } else { Out.Slave << Quote; NEscJ::EscapeJ(s, Out.Slave, Safe, Unsafe); Out.Slave << Quote; } } bool OnString(const TStringBuf& s) override { OnVal(); WriteString(s); AfterVal(); return true; } bool OnOpen(char c) { OnVal(); Level++; Out.Hold(c); return true; } bool OnOpenMap() override { return OnOpen('{'); } bool OnOpenArray() override { return OnOpen('['); } bool OnMapKey(const TStringBuf& k) override { OnVal(); WriteString(k); WriteSpace(' '); Out.Hold(':'); return true; } bool OnClose(char c) { if (!Level) return false; Level--; if (Out.Dirty && c == Out.Last) { WriteSpace(' '); } else { Out.Revert(); Pad(true); } return true; } bool OnCloseMap() override { if (!OnClose('{')) return false; Out.Write("}"); AfterVal(); return true; } bool OnCloseArray() override { if (!OnClose('[')) return false; Out.Write("]"); AfterVal(); return true; } bool OnEnd() override { return !Level; } }; bool TJsonPrettifier::Prettify(TStringBuf in, IOutputStream& out) const { TPrettifier p(out, *this); if (Strict) { TMemoryInput mIn(in.data(), in.size()); return ReadJson(&mIn, &p); } else { return ReadJsonFast(in, &p); } } TString TJsonPrettifier::Prettify(TStringBuf in) const { TStringStream s; if (Prettify(in, s)) return s.Str(); return TString(); } }