diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/json/json_prettifier.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/json/json_prettifier.cpp')
-rw-r--r-- | library/cpp/json/json_prettifier.cpp | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/library/cpp/json/json_prettifier.cpp b/library/cpp/json/json_prettifier.cpp new file mode 100644 index 0000000000..bb16aab44e --- /dev/null +++ b/library/cpp/json/json_prettifier.cpp @@ -0,0 +1,277 @@ +#include "json_prettifier.h" + +#include <util/generic/deque.h> +#include <util/generic/algorithm.h> +#include <util/memory/pool.h> +#include <util/string/util.h> + +#include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h> + +namespace NJson { + struct TRewritableOut { + IOutputStream& Slave; + + char Last = 0; + bool Dirty = false; + + TRewritableOut(IOutputStream& sl) + : Slave(sl) + { + } + + template <typename T> + 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 <typename T> + 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<false, true>(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(); + } + +} |