#include <library/cpp/json/fast_sax/unescape.h>
#include <library/cpp/json/fast_sax/parser.h>
#include <util/string/cast.h>
#include <util/generic/buffer.h>
#include <util/generic/strbuf.h>
#include <util/generic/ymath.h>
namespace NJson {
enum EStoredStr {
SS_NONE = 0, SS_NOCOPY, SS_MUSTCOPY
};
struct TParserCtx {
TJsonCallbacks& Hndl;
TBuffer Buffer;
TStringBuf String;
EStoredStr Stored = SS_NONE;
bool ExpectValue = true;
const char* p0 = nullptr;
const char* p = nullptr;
const char* pe = nullptr;
const char* eof = nullptr;
const char* ts = nullptr;
const char* te = nullptr;
int cs = 0;
int act = 0;
TParserCtx(TJsonCallbacks& h, TStringBuf data)
: Hndl(h)
, p0(data.data())
, p(data.data())
, pe(data.end())
, eof(data.end())
{}
static inline bool GoodPtrs(const char* b, const char* e) {
return b && e && b <= e;
}
bool OnError(TStringBuf reason = TStringBuf(""), bool end = false) const {
size_t off = 0;
TStringBuf token;
if (GoodPtrs(p0, ts)) {
off = ts - p0;
} else if (end && GoodPtrs(p0, pe)) {
off = pe - p0;
}
if (GoodPtrs(ts, te)) {
token = TStringBuf(ts, te);
}
if (!token) {
Hndl.OnError(off, reason);
} else {
Hndl.OnError(off, TString::Join(reason, " at token: '", token, "'"));
}
return false;
}
bool OnVal() {
if (Y_UNLIKELY(!ExpectValue)) {
return false;
}
ExpectValue = false;
return true;
}
bool OnNull() {
return Y_LIKELY(OnVal())
&& Hndl.OnNull();
}
bool OnTrue() {
return Y_LIKELY(OnVal())
&& Hndl.OnBoolean(true);
}
bool OnFalse() {
return Y_LIKELY(OnVal())
&& Hndl.OnBoolean(false);
}
bool OnPInt() {
unsigned long long res = 0;
return Y_LIKELY(OnVal())
&& TryFromString<unsigned long long>(TStringBuf(ts, te), res)
&& Hndl.OnUInteger(res);
}
bool OnNInt() {
long long res = 0;
return Y_LIKELY(OnVal())
&& TryFromString<long long>(TStringBuf(ts, te), res)
&& Hndl.OnInteger(res);
}
bool OnFlt() {
double res = 0;
return Y_LIKELY(OnVal())
&& TryFromString<double>(TStringBuf(ts, te), res)
&& IsFinite(res)
&& Hndl.OnDouble(res);
}
bool OnMapOpen() {
bool res = Y_LIKELY(OnVal())
&& Hndl.OnOpenMap();
ExpectValue = true;
return res;
}
bool OnArrOpen() {
bool res = Y_LIKELY(OnVal())
&& Hndl.OnOpenArray();
ExpectValue = true;
return res;
}
bool OnString(TStringBuf s, EStoredStr t) {
if (Y_LIKELY(OnVal())) {
String = s;
Stored = t;
return true;
} else {
return false;
}
}
bool OnStrU() {
return OnString(TStringBuf(ts, te), SS_NOCOPY);
}
bool OnStrQ() {
return OnString(TStringBuf(ts + 1, te - 1), SS_NOCOPY);
}
bool OnStrE() {
Buffer.Clear();
Buffer.Reserve(2 * (te - ts));
return OnString(UnescapeJsonUnicode(TStringBuf(ts + 1, te - ts - 2), Buffer.data()), SS_MUSTCOPY);
}
bool OnMapClose() {
ExpectValue = false;
return Y_LIKELY(OnAfterVal())
&& Hndl.OnCloseMap();
}
bool OnArrClose() {
ExpectValue = false;
return Y_LIKELY(OnAfterVal())
&& Hndl.OnCloseArray();
}
bool OnColon() {
if (ExpectValue) {
return false;
}
ExpectValue = true;
const auto stored = Stored;
Stored = SS_NONE;
switch (stored) {
default:
return false;
case SS_NOCOPY:
return Hndl.OnMapKeyNoCopy(String);
case SS_MUSTCOPY:
return Hndl.OnMapKey(String);
}
}
bool OnAfterVal() {
const auto stored = Stored;
Stored = SS_NONE;
switch (stored) {
default:
return true;
case SS_NOCOPY:
return Hndl.OnStringNoCopy(String);
case SS_MUSTCOPY:
return Hndl.OnString(String);
}
}
bool OnComma() {
if (Y_UNLIKELY(ExpectValue)) {
return false;
}
ExpectValue = true;
return OnAfterVal();
}
bool Parse();
};
#if 0
%%{
machine fastjson;
alphtype char;
action OnNull { if (Y_UNLIKELY(!OnNull())) goto TOKEN_ERROR; }
action OnTrue { if (Y_UNLIKELY(!OnTrue())) goto TOKEN_ERROR; }
action OnFalse { if (Y_UNLIKELY(!OnFalse())) goto TOKEN_ERROR; }
action OnPInt { if (Y_UNLIKELY(!OnPInt())) goto TOKEN_ERROR; }
action OnNInt { if (Y_UNLIKELY(!OnNInt())) goto TOKEN_ERROR; }
action OnFlt { if (Y_UNLIKELY(!OnFlt())) goto TOKEN_ERROR; }
action OnStrU { if (Y_UNLIKELY(!OnStrU())) goto TOKEN_ERROR; }
action OnStrQ { if (Y_UNLIKELY(!OnStrQ())) goto TOKEN_ERROR; }
action OnStrE { if (Y_UNLIKELY(!OnStrE())) goto TOKEN_ERROR; }
action OnDictO { if (Y_UNLIKELY(!OnMapOpen())) goto TOKEN_ERROR; }
action OnDictC { if (Y_UNLIKELY(!OnMapClose())) goto TOKEN_ERROR; }
action OnArrO { if (Y_UNLIKELY(!OnArrOpen())) goto TOKEN_ERROR; }
action OnArrC { if (Y_UNLIKELY(!OnArrClose())) goto TOKEN_ERROR; }
action OnComma { if (Y_UNLIKELY(!OnComma())) goto TOKEN_ERROR; }
action OnColon { if (Y_UNLIKELY(!OnColon())) goto TOKEN_ERROR; }
action OnError { goto TOKEN_ERROR; }
comment1 = "/*" (any* -- "*/") "*/";
pint = [0-9]+;
nint = '-'[0-9]+;
flt = '-'?[0-9.][0-9.eE+\-]+;
uchar0 = [a-zA-Z_@$] | (0x80 .. 0xFF);
uchar = uchar0 | digit | [.\-];
qchar = [^'\\]; #';
dchar = [^"\\]; #";
echar = "\\" any;
qechar = qchar | echar;
dechar = dchar | echar;
strq = "'" qchar* "'";
strd = '"' dchar* '"';
strqe = "'" qechar* "'";
strde = '"' dechar* '"';
strU = uchar0 uchar*;
strQ = strq | strd;
strE = strqe | strde;
ws = (0x00 .. 0x20) | 0x7F;
sp = ws+;
main := |*
'null' => OnNull;
'true' => OnTrue;
'false' => OnFalse;
pint => OnPInt;
nint => OnNInt;
flt => OnFlt;
strU => OnStrU;
strQ => OnStrQ;
strE => OnStrE;
',' => OnComma;
':' => OnColon;
'{' => OnDictO;
'}' => OnDictC;
'[' => OnArrO;
']' => OnArrC;
sp;
comment1;
(flt | pint | nint) (any - (ws | ',' | ':' | '{' | '}' | '[' | ']')) => OnError;
any => OnError;
*|;
}%%
#endif
bool TParserCtx::Parse() {
try {
%%{
write data noerror nofinal;
write init;
write exec;
}%%
Y_UNUSED(fastjson_en_main);
} catch (const TFromStringException& e) {
return OnError(e.what());
}
return OnAfterVal() && Hndl.OnEnd() || OnError("invalid or truncated", true);
TOKEN_ERROR:
return OnError("invalid syntax");
}
bool ReadJsonFast(TStringBuf data, TJsonCallbacks* h) {
return TParserCtx(*h, data).Parse();
}
}