forked from 14134597/fast_json
846 lines
32 KiB
MQL5
846 lines
32 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| fast_json.mqh |
|
||
|
|
//| AI Toolkit - Neural Execution Unified System |
|
||
|
|
//| |
|
||
|
|
//| Module: Json (The "War Machine" Edition V3.8) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "AIToolkit Framework"
|
||
|
|
#property version "3.80"
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Constants & Masks |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#define J_NULL 0x00
|
||
|
|
#define J_BOOL 0x01
|
||
|
|
#define J_INT 0x02
|
||
|
|
#define J_DBL 0x03
|
||
|
|
#define J_STR 0x04
|
||
|
|
#define J_ARR 0x05
|
||
|
|
#define J_OBJ 0x06
|
||
|
|
#define J_KEY 0x07
|
||
|
|
|
||
|
|
#define SWAR_LO 0x0101010101010101
|
||
|
|
#define SWAR_HI 0x8080808080808080
|
||
|
|
#define SWAR_QUOTE 0x2222222222222222
|
||
|
|
#define SWAR_SLASH 0x5C5C5C5C5C5C5C5C
|
||
|
|
|
||
|
|
#define CC_WHITE 1
|
||
|
|
#define CC_STRUCT 2
|
||
|
|
#define CC_QUOTE 3
|
||
|
|
#define CC_DIGIT 4
|
||
|
|
#define CC_OTHER 0
|
||
|
|
|
||
|
|
#define ST_VAL 0
|
||
|
|
#define ST_ARR 1
|
||
|
|
#define ST_OBJ 2
|
||
|
|
#define ST_KEY 3
|
||
|
|
|
||
|
|
#define MACRO_GET_STEP(idx) ((int)(tape[idx] & 0xFFFFFFFF))
|
||
|
|
|
||
|
|
enum EnumJsonError {
|
||
|
|
JSON_OK = 0,
|
||
|
|
JSON_ERR_INVALID_CHAR,
|
||
|
|
JSON_ERR_UNEXPECTED_END,
|
||
|
|
JSON_ERR_STACK_OVERFLOW,
|
||
|
|
JSON_ERR_INVALID_NUMBER,
|
||
|
|
JSON_ERR_EXPECTED_KEY,
|
||
|
|
JSON_ERR_EXPECTED_COLON,
|
||
|
|
JSON_ERR_WRONG_TYPE
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Globals (Lookup Tables) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
uchar g_cc[256];
|
||
|
|
bool g_init = false;
|
||
|
|
|
||
|
|
double g_Exp10[309];
|
||
|
|
double g_Pow10[309];
|
||
|
|
uchar g_digit_pairs[200];
|
||
|
|
uchar g_hex[256];
|
||
|
|
|
||
|
|
void InitTables() {
|
||
|
|
if(g_init)
|
||
|
|
return;
|
||
|
|
ArrayInitialize(g_cc, CC_OTHER);
|
||
|
|
g_cc[' '] = CC_WHITE; g_cc['\t'] = CC_WHITE; g_cc['\r'] = CC_WHITE; g_cc['\n'] = CC_WHITE;
|
||
|
|
g_cc['{'] = CC_STRUCT; g_cc['}'] = CC_STRUCT; g_cc['['] = CC_STRUCT; g_cc[']'] = CC_STRUCT;
|
||
|
|
g_cc[':'] = CC_STRUCT; g_cc[','] = CC_STRUCT; g_cc['"'] = CC_QUOTE;
|
||
|
|
g_cc['0'] = CC_DIGIT; g_cc['1'] = CC_DIGIT; g_cc['2'] = CC_DIGIT; g_cc['3'] = CC_DIGIT;
|
||
|
|
g_cc['4'] = CC_DIGIT; g_cc['5'] = CC_DIGIT; g_cc['6'] = CC_DIGIT; g_cc['7'] = CC_DIGIT;
|
||
|
|
g_cc['8'] = CC_DIGIT; g_cc['9'] = CC_DIGIT; g_cc['-'] = CC_DIGIT;
|
||
|
|
|
||
|
|
g_Exp10[0] = 1.0;
|
||
|
|
g_Pow10[0] = 1.0;
|
||
|
|
for(int i = 1; i < 309; i++) {
|
||
|
|
g_Pow10[i] = g_Pow10[i - 1] * 10.0;
|
||
|
|
g_Exp10[i] = 1.0 / g_Pow10[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
for(int i = 0; i < 100; i++) {
|
||
|
|
g_digit_pairs[i * 2] = (uchar)('0' + i / 10);
|
||
|
|
g_digit_pairs[i * 2 + 1] = (uchar)('0' + i % 10);
|
||
|
|
}
|
||
|
|
|
||
|
|
ArrayInitialize(g_hex, 0);
|
||
|
|
for(int i = '0'; i <= '9'; i++) g_hex[i] = (uchar)(i - '0');
|
||
|
|
for(int i = 'a'; i <= 'f'; i++) g_hex[i] = (uchar)(i - 'a' + 10);
|
||
|
|
for(int i = 'A'; i <= 'F'; i++) g_hex[i] = (uchar)(i - 'A' + 10);
|
||
|
|
|
||
|
|
g_init = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CJsonContext: The Engine |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CJsonContext {
|
||
|
|
public:
|
||
|
|
long tape[];
|
||
|
|
int tape_pos;
|
||
|
|
uchar buffer[];
|
||
|
|
int len;
|
||
|
|
|
||
|
|
int last_error;
|
||
|
|
string error_msg;
|
||
|
|
int err_pos;
|
||
|
|
|
||
|
|
int stack_node[512];
|
||
|
|
int stack_state[512];
|
||
|
|
int stack_count[512];
|
||
|
|
int sp;
|
||
|
|
|
||
|
|
int GetType(int idx) { return (int)((tape[idx] >> 56) & 0xFF); }
|
||
|
|
long GetSize(int idx) { return tape[idx] & 0xFFFFFFFF; }
|
||
|
|
int GetCount(int idx) { return (int)((tape[idx] >> 32) & 0xFFFFFF); }
|
||
|
|
bool GetBool(int idx) { return (tape[idx + 1] & 1) == 1; }
|
||
|
|
long GetInt(int idx) { return tape[idx + 1]; }
|
||
|
|
|
||
|
|
double GetDouble(int idx) {
|
||
|
|
union U { double d; long l; } u;
|
||
|
|
u.l = tape[idx + 1];
|
||
|
|
return u.d;
|
||
|
|
}
|
||
|
|
|
||
|
|
string GetStr(int idx) {
|
||
|
|
if(idx < 0 || idx >= tape_pos) return "";
|
||
|
|
long data = tape[idx + 1];
|
||
|
|
return Unescape((int)(data >> 32), (int)(data & 0xFFFFFFFF));
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Reserve(int size) {
|
||
|
|
int current = ArraySize(tape);
|
||
|
|
if(current < size) {
|
||
|
|
int geometric = current + (current >> 1);
|
||
|
|
int new_cap = (size > geometric) ? size + 4096 : geometric;
|
||
|
|
if(ArrayResize(tape, new_cap) == -1) {
|
||
|
|
last_error = JSON_ERR_STACK_OVERFLOW;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int SkipWS(int ptr) {
|
||
|
|
while(ptr < len && buffer[ptr] <= ' ') ptr++;
|
||
|
|
return ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
void GetErrorLocation(int ptr, int &err_line, int &err_col) {
|
||
|
|
err_line = 1; err_col = 1;
|
||
|
|
for(int i = 0; i < ptr && i < len; i++) {
|
||
|
|
if(buffer[i] == '\n') { err_line++; err_col = 1; } else err_col++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int ScanString(int ptr) {
|
||
|
|
int start = ptr;
|
||
|
|
int swar_limit = len - 8;
|
||
|
|
|
||
|
|
while(ptr <= swar_limit) {
|
||
|
|
// Inlined Load64 expressions for raw performance
|
||
|
|
ulong word = (ulong)buffer[ptr] | ((ulong)buffer[ptr + 1] << 8) |
|
||
|
|
((ulong)buffer[ptr + 2] << 16) | ((ulong)buffer[ptr + 3] << 24) |
|
||
|
|
((ulong)buffer[ptr + 4] << 32) | ((ulong)buffer[ptr + 5] << 40) |
|
||
|
|
((ulong)buffer[ptr + 6] << 48) | ((ulong)buffer[ptr + 7] << 56);
|
||
|
|
ulong z_q = ((word ^ SWAR_QUOTE) - SWAR_LO) & ~(word ^ SWAR_QUOTE) & SWAR_HI;
|
||
|
|
ulong z_s = ((word ^ SWAR_SLASH) - SWAR_LO) & ~(word ^ SWAR_SLASH) & SWAR_HI;
|
||
|
|
if((z_q | z_s) != 0) break;
|
||
|
|
ptr += 8;
|
||
|
|
}
|
||
|
|
|
||
|
|
while(ptr < len) {
|
||
|
|
uchar c = buffer[ptr];
|
||
|
|
if(c == '"') return ptr - start;
|
||
|
|
if(c == '\\') { ptr += 2; continue; }
|
||
|
|
ptr++;
|
||
|
|
}
|
||
|
|
return ptr - start;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool GetInteger(long &Value, int &cur, uchar &c) {
|
||
|
|
if(c != ('-' ^ '0')) {
|
||
|
|
Value = c;
|
||
|
|
while(++cur < len) {
|
||
|
|
c = buffer[cur] ^ '0';
|
||
|
|
if(c <= 9) Value = Value * 10 + c; else break;
|
||
|
|
}
|
||
|
|
return (Value >= 0);
|
||
|
|
} else if(++cur < len) {
|
||
|
|
c = buffer[cur] ^ '0';
|
||
|
|
if(c <= 9) {
|
||
|
|
Value = -(long)c;
|
||
|
|
while(++cur < len) {
|
||
|
|
c = buffer[cur] ^ '0';
|
||
|
|
if(c <= 9) Value = Value * 10 - c; else break;
|
||
|
|
}
|
||
|
|
return (Value <= 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ParseNumber(int &cur, uchar &c) {
|
||
|
|
int start = cur;
|
||
|
|
long int_val;
|
||
|
|
c ^= '0';
|
||
|
|
|
||
|
|
if(this.GetInteger(int_val, cur, c)) {
|
||
|
|
if(c == ('.' ^ '0')) {
|
||
|
|
if(++cur < len) {
|
||
|
|
c = buffer[cur] ^ '0';
|
||
|
|
if(c <= 9) {
|
||
|
|
long frac_int;
|
||
|
|
this.GetInteger(frac_int, cur, c);
|
||
|
|
if(c == ('e' ^ '0') || c == ('E' ^ '0')) {
|
||
|
|
long exp_val;
|
||
|
|
if(++cur < len) { c = buffer[cur] ^ '0'; this.GetInteger(exp_val, cur, c); }
|
||
|
|
}
|
||
|
|
int l = cur - start;
|
||
|
|
tape[tape_pos++] = ((long)J_DBL << 56) | 2;
|
||
|
|
tape[tape_pos++] = ((long)start << 32) | (long)l;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if(c == ('e' ^ '0') || c == ('E' ^ '0')) {
|
||
|
|
long exp_val;
|
||
|
|
if(++cur < len) { c = buffer[cur] ^ '0'; this.GetInteger(exp_val, cur, c); }
|
||
|
|
int l = cur - start;
|
||
|
|
tape[tape_pos++] = ((long)J_DBL << 56) | 2;
|
||
|
|
tape[tape_pos++] = ((long)start << 32) | (long)l;
|
||
|
|
return true;
|
||
|
|
} else {
|
||
|
|
tape[tape_pos++] = ((long)J_INT << 56) | 2;
|
||
|
|
tape[tape_pos++] = int_val;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
string Unescape(int ptr, int str_len) {
|
||
|
|
int i = 0;
|
||
|
|
while(i + 8 <= str_len) {
|
||
|
|
int curr_p = ptr + i;
|
||
|
|
ulong word = (ulong)buffer[curr_p] | ((ulong)buffer[curr_p + 1] << 8) |
|
||
|
|
((ulong)buffer[curr_p + 2] << 16) | ((ulong)buffer[curr_p + 3] << 24) |
|
||
|
|
((ulong)buffer[curr_p + 4] << 32) | ((ulong)buffer[curr_p + 5] << 40) |
|
||
|
|
((ulong)buffer[curr_p + 6] << 48) | ((ulong)buffer[curr_p + 7] << 56);
|
||
|
|
ulong x_s = word ^ SWAR_SLASH;
|
||
|
|
ulong z_s = (x_s - SWAR_LO) & ~x_s & SWAR_HI;
|
||
|
|
if(z_s != 0) break;
|
||
|
|
i += 8;
|
||
|
|
}
|
||
|
|
for(; i < str_len; i++) {
|
||
|
|
if(buffer[ptr + i] == '\\') break;
|
||
|
|
}
|
||
|
|
if(i >= str_len)
|
||
|
|
return CharArrayToString(buffer, ptr, str_len, CP_UTF8);
|
||
|
|
|
||
|
|
ushort res[];
|
||
|
|
ArrayResize(res, str_len);
|
||
|
|
int pos = 0;
|
||
|
|
for(i = 0; i < str_len; i++) {
|
||
|
|
uchar c = buffer[ptr + i];
|
||
|
|
if(c == '\\' && i + 1 < str_len) {
|
||
|
|
i++;
|
||
|
|
uchar next = buffer[ptr + i];
|
||
|
|
switch (next) {
|
||
|
|
case '"': res[pos++] = '"'; break;
|
||
|
|
case '\\': res[pos++] = '\\'; break;
|
||
|
|
case '/': res[pos++] = '/'; break;
|
||
|
|
case 'b': res[pos++] = '\x08'; break;
|
||
|
|
case 'f': res[pos++] = '\f'; break;
|
||
|
|
case 'n': res[pos++] = '\n'; break;
|
||
|
|
case 'r': res[pos++] = '\r'; break;
|
||
|
|
case 't': res[pos++] = '\t'; break;
|
||
|
|
case 'u': {
|
||
|
|
if(i + 4 < str_len) {
|
||
|
|
int code = ((int)g_hex[buffer[ptr + i + 1]] << 12) |
|
||
|
|
((int)g_hex[buffer[ptr + i + 2]] << 8) |
|
||
|
|
((int)g_hex[buffer[ptr + i + 3]] << 4) |
|
||
|
|
(int)g_hex[buffer[ptr + i + 4]];
|
||
|
|
res[pos++] = (ushort)code;
|
||
|
|
i += 4;
|
||
|
|
} else res[pos++] = 'u';
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
default: res[pos++] = next; break;
|
||
|
|
}
|
||
|
|
} else res[pos++] = c;
|
||
|
|
}
|
||
|
|
return ShortArrayToString(res, 0, pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
bool Parse(string json_str) {
|
||
|
|
if(!g_init) InitTables();
|
||
|
|
tape_pos = 0; sp = 0; last_error = JSON_OK;
|
||
|
|
int str_len = StringLen(json_str);
|
||
|
|
if(ArraySize(buffer) < str_len + 8) ArrayResize(buffer, str_len + 8, str_len + 4096);
|
||
|
|
int wr = StringToCharArray(json_str, buffer, 0, WHOLE_ARRAY, CP_UTF8);
|
||
|
|
len = (wr > 0 && buffer[wr - 1] == 0) ? wr - 1 : wr;
|
||
|
|
return _DoParse();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool ParseBuffer(uchar &data[], int data_len) {
|
||
|
|
if(!g_init) InitTables();
|
||
|
|
tape_pos = 0; sp = 0; last_error = JSON_OK;
|
||
|
|
if(ArraySize(buffer) < data_len + 8) ArrayResize(buffer, data_len + 8, data_len + 4096);
|
||
|
|
ArrayCopy(buffer, data, 0, 0, data_len);
|
||
|
|
len = data_len;
|
||
|
|
if(len > 0 && buffer[len - 1] == 0) len--;
|
||
|
|
return _DoParse();
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
bool _DoParse() {
|
||
|
|
if(!Reserve(len * 2 + 1024)) return false;
|
||
|
|
int cur = 0;
|
||
|
|
stack_state[sp] = ST_VAL; stack_node[sp] = -1; stack_count[sp] = 0; sp++;
|
||
|
|
int key_ptr = 0; int key_len = 0; uint key_hash = 0;
|
||
|
|
|
||
|
|
while(sp > 0) {
|
||
|
|
if(!Reserve(tape_pos + 32)) return false;
|
||
|
|
cur = SkipWS(cur);
|
||
|
|
if(cur >= len) break;
|
||
|
|
|
||
|
|
uchar c = buffer[cur];
|
||
|
|
int state = stack_state[sp - 1];
|
||
|
|
|
||
|
|
if(state == ST_VAL) {
|
||
|
|
if(sp > 1) stack_count[sp - 2]++;
|
||
|
|
if(key_len > 0) {
|
||
|
|
tape[tape_pos++] = ((long)J_KEY << 56);
|
||
|
|
tape[tape_pos++] = ((long)key_ptr << 32) | ((long)(key_len & 0xFF) << 24) | (long)(key_hash & 0xFFFFFF);
|
||
|
|
key_len = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(c == '"') {
|
||
|
|
cur++; int start = cur; int l = ScanString(cur); cur += l + 1;
|
||
|
|
tape[tape_pos++] = ((long)J_STR << 56) | 2;
|
||
|
|
tape[tape_pos++] = ((long)start << 32) | (long)l;
|
||
|
|
sp--;
|
||
|
|
} else if((c ^ '0') <= 9 || c == '-') {
|
||
|
|
if(!ParseNumber(cur, c)) return false;
|
||
|
|
sp--;
|
||
|
|
} else if(c == '{') {
|
||
|
|
if(sp >= 512) { last_error = JSON_ERR_STACK_OVERFLOW; err_pos = cur; return false; }
|
||
|
|
int idx = tape_pos++; stack_state[sp - 1] = ST_OBJ; stack_node[sp - 1] = idx; stack_count[sp - 1] = 0; cur++;
|
||
|
|
} else if(c == '[') {
|
||
|
|
if(sp >= 512) { last_error = JSON_ERR_STACK_OVERFLOW; err_pos = cur; return false; }
|
||
|
|
int idx = tape_pos++; stack_state[sp - 1] = ST_ARR; stack_node[sp - 1] = idx; stack_count[sp - 1] = 0; cur++;
|
||
|
|
} else if(c == 't') {
|
||
|
|
cur += 4; tape[tape_pos++] = ((long)J_BOOL << 56) | 2; tape[tape_pos++] = 1; sp--;
|
||
|
|
} else if(c == 'f') {
|
||
|
|
cur += 5; tape[tape_pos++] = ((long)J_BOOL << 56) | 2; tape[tape_pos++] = 0; sp--;
|
||
|
|
} else if(c == 'n') {
|
||
|
|
cur += 4; tape[tape_pos++] = ((long)J_NULL << 56) | 2; tape[tape_pos++] = 0ULL; sp--;
|
||
|
|
} else {
|
||
|
|
last_error = JSON_ERR_INVALID_CHAR; err_pos = cur; return false;
|
||
|
|
}
|
||
|
|
} else if(state == ST_OBJ) {
|
||
|
|
if(c == '}') {
|
||
|
|
int idx = stack_node[sp - 1];
|
||
|
|
tape[idx] = ((long)J_OBJ << 56) | ((long)(stack_count[sp - 1] & 0xFFFFFF) << 32) | ((tape_pos - idx) & 0xFFFFFFFF);
|
||
|
|
cur++; sp--;
|
||
|
|
} else {
|
||
|
|
if(c == ',') { cur++; continue; }
|
||
|
|
if(c != '"') { last_error = JSON_ERR_EXPECTED_KEY; err_pos = cur; return false; }
|
||
|
|
cur++; key_ptr = cur; key_len = ScanString(cur); cur += key_len + 1;
|
||
|
|
|
||
|
|
key_hash = 2166136261; int ki = 0;
|
||
|
|
for(; ki + 4 <= key_len; ki += 4) {
|
||
|
|
key_hash = (key_hash ^ buffer[key_ptr + ki]) * 16777619;
|
||
|
|
key_hash = (key_hash ^ buffer[key_ptr + ki + 1]) * 16777619;
|
||
|
|
key_hash = (key_hash ^ buffer[key_ptr + ki + 2]) * 16777619;
|
||
|
|
key_hash = (key_hash ^ buffer[key_ptr + ki + 3]) * 16777619;
|
||
|
|
}
|
||
|
|
for(; ki < key_len; ki++) key_hash = (key_hash ^ buffer[key_ptr + ki]) * 16777619;
|
||
|
|
|
||
|
|
cur = SkipWS(cur);
|
||
|
|
if(buffer[cur] != ':') { last_error = JSON_ERR_EXPECTED_COLON; err_pos = cur; return false; }
|
||
|
|
cur++; stack_state[sp] = ST_VAL; stack_node[sp] = -1; sp++;
|
||
|
|
}
|
||
|
|
} else if(state == ST_ARR) {
|
||
|
|
if(c == ']') {
|
||
|
|
int idx = stack_node[sp - 1];
|
||
|
|
tape[idx] = ((long)J_ARR << 56) | ((long)(stack_count[sp - 1] & 0xFFFFFF) << 32) | ((tape_pos - idx) & 0xFFFFFFFF);
|
||
|
|
cur++; sp--;
|
||
|
|
} else {
|
||
|
|
if(c == ',') { cur++; continue; }
|
||
|
|
stack_state[sp] = ST_VAL; stack_node[sp] = -1; sp++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return (last_error == JSON_OK);
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
string Serialize(bool pretty) {
|
||
|
|
if(tape_pos == 0) return "";
|
||
|
|
uchar out[]; int cap = len * 2; if(cap < 1024) cap = 1024;
|
||
|
|
ArrayResize(out, cap); int pos = 0;
|
||
|
|
if(pretty) WriteNodePretty(0, out, pos, cap, 0); else WriteNode(0, out, pos, cap);
|
||
|
|
return CharArrayToString(out, 0, pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
void WriteNode(int idx, uchar &out[], int &pos, int &cap) {
|
||
|
|
if(idx >= tape_pos) return;
|
||
|
|
switch (GetType(idx)) {
|
||
|
|
case J_NULL:
|
||
|
|
CheckCap(4, pos, cap, out); out[pos++] = 'n'; out[pos++] = 'u'; out[pos++] = 'l'; out[pos++] = 'l'; break;
|
||
|
|
case J_BOOL:
|
||
|
|
if(GetBool(idx)) { CheckCap(4, pos, cap, out); out[pos++] = 't'; out[pos++] = 'r'; out[pos++] = 'u'; out[pos++] = 'e'; }
|
||
|
|
else { CheckCap(5, pos, cap, out); out[pos++] = 'f'; out[pos++] = 'a'; out[pos++] = 'l'; out[pos++] = 's'; out[pos++] = 'e'; }
|
||
|
|
break;
|
||
|
|
case J_INT: PutRawInteger(GetInt(idx), out, pos, cap); break;
|
||
|
|
case J_DBL: PutRawDouble(idx, out, pos, cap); break;
|
||
|
|
case J_STR: {
|
||
|
|
long data = tape[idx + 1]; int p = (int)(data >> 32); int l = (int)(data & 0xFFFFFFFF);
|
||
|
|
CheckCap(l + 2, pos, cap, out); out[pos++] = '"'; ArrayCopy(out, buffer, pos, p, l); pos += l; out[pos++] = '"'; break;
|
||
|
|
}
|
||
|
|
case J_ARR: {
|
||
|
|
PutChar('[', out, pos, cap); int count = GetCount(idx); int cur = idx + 1;
|
||
|
|
for(int i = 0; i < count; i++) {
|
||
|
|
if(i > 0) PutChar(',', out, pos, cap);
|
||
|
|
WriteNode(cur, out, pos, cap); cur += MACRO_GET_STEP(cur);
|
||
|
|
}
|
||
|
|
PutChar(']', out, pos, cap); break;
|
||
|
|
}
|
||
|
|
case J_OBJ: {
|
||
|
|
PutChar('{', out, pos, cap); int count = GetCount(idx); int cur = idx + 1; int emitted = 0;
|
||
|
|
while(emitted < count) {
|
||
|
|
if(GetType(cur) == J_KEY) {
|
||
|
|
if(emitted > 0) PutChar(',', out, pos, cap);
|
||
|
|
long kp = tape[cur + 1]; int kptr = (int)(kp >> 32); int klen = (int)((kp >> 24) & 0xFF);
|
||
|
|
CheckCap(klen + 3, pos, cap, out); out[pos++] = '"'; ArrayCopy(out, buffer, pos, kptr, klen); pos += klen; out[pos++] = '"'; out[pos++] = ':';
|
||
|
|
int val_idx = cur + 2; WriteNode(val_idx, out, pos, cap); cur = val_idx + (int)(tape[val_idx] & 0xFFFFFFFF); emitted++;
|
||
|
|
} else cur++;
|
||
|
|
}
|
||
|
|
PutChar('}', out, pos, cap); break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void WriteNodePretty(int idx, uchar &out[], int &pos, int &cap, int depth) {
|
||
|
|
if(idx >= tape_pos) return;
|
||
|
|
switch (GetType(idx)) {
|
||
|
|
case J_NULL:
|
||
|
|
CheckCap(4, pos, cap, out); out[pos++] = 'n'; out[pos++] = 'u'; out[pos++] = 'l'; out[pos++] = 'l'; break;
|
||
|
|
case J_BOOL:
|
||
|
|
if(GetBool(idx)) { CheckCap(4, pos, cap, out); out[pos++] = 't'; out[pos++] = 'r'; out[pos++] = 'u'; out[pos++] = 'e'; }
|
||
|
|
else { CheckCap(5, pos, cap, out); out[pos++] = 'f'; out[pos++] = 'a'; out[pos++] = 'l'; out[pos++] = 's'; out[pos++] = 'e'; }
|
||
|
|
break;
|
||
|
|
case J_INT: PutRawInteger(GetInt(idx), out, pos, cap); break;
|
||
|
|
case J_DBL: PutRawDouble(idx, out, pos, cap); break;
|
||
|
|
case J_STR: {
|
||
|
|
long data = tape[idx + 1]; int p = (int)(data >> 32); int l = (int)(data & 0xFFFFFFFF);
|
||
|
|
CheckCap(l + 2, pos, cap, out); out[pos++] = '"'; ArrayCopy(out, buffer, pos, p, l); pos += l; out[pos++] = '"'; break;
|
||
|
|
}
|
||
|
|
case J_ARR: {
|
||
|
|
int count = GetCount(idx); if(count == 0) { CheckCap(2, pos, cap, out); out[pos++] = '['; out[pos++] = ']'; return; }
|
||
|
|
PutChar('[', out, pos, cap); PutChar('\n', out, pos, cap); int cur = idx + 1;
|
||
|
|
for(int i = 0; i < count; i++) {
|
||
|
|
if(i > 0) { PutChar(',', out, pos, cap); PutChar('\n', out, pos, cap); }
|
||
|
|
Indent(depth + 1, out, pos, cap); WriteNodePretty(cur, out, pos, cap, depth + 1); cur += MACRO_GET_STEP(cur);
|
||
|
|
}
|
||
|
|
PutChar('\n', out, pos, cap); Indent(depth, out, pos, cap); PutChar(']', out, pos, cap); break;
|
||
|
|
}
|
||
|
|
case J_OBJ: {
|
||
|
|
int count = GetCount(idx); if(count == 0) { CheckCap(2, pos, cap, out); out[pos++] = '{'; out[pos++] = '}'; return; }
|
||
|
|
PutChar('{', out, pos, cap); PutChar('\n', out, pos, cap); int cur = idx + 1; int emitted = 0;
|
||
|
|
while(emitted < count) {
|
||
|
|
if(GetType(cur) == J_KEY) {
|
||
|
|
if(emitted > 0) { PutChar(',', out, pos, cap); PutChar('\n', out, pos, cap); }
|
||
|
|
Indent(depth + 1, out, pos, cap);
|
||
|
|
long kp = tape[cur + 1]; int kptr = (int)(kp >> 32); int klen = (int)((kp >> 24) & 0xFF);
|
||
|
|
CheckCap(klen + 4, pos, cap, out); out[pos++] = '"'; ArrayCopy(out, buffer, pos, kptr, klen); pos += klen; out[pos++] = '"'; out[pos++] = ':'; out[pos++] = ' ';
|
||
|
|
int val_idx = cur + 2; WriteNodePretty(val_idx, out, pos, cap, depth + 1); cur = val_idx + (int)(tape[val_idx] & 0xFFFFFFFF); emitted++;
|
||
|
|
} else cur++;
|
||
|
|
}
|
||
|
|
PutChar('\n', out, pos, cap); Indent(depth, out, pos, cap); PutChar('}', out, pos, cap); break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Indent(int depth, uchar &out[], int &pos, int &cap) {
|
||
|
|
CheckCap(depth * 3, pos, cap, out);
|
||
|
|
for(int i = 0; i < depth; i++) { out[pos++] = ' '; out[pos++] = ' '; out[pos++] = ' '; }
|
||
|
|
}
|
||
|
|
|
||
|
|
void PutChar(uchar c, uchar &out[], int &pos, int &cap) {
|
||
|
|
if(pos >= cap) { cap = (int)(cap * 1.5) + 32; ArrayResize(out, cap); }
|
||
|
|
out[pos++] = c;
|
||
|
|
}
|
||
|
|
|
||
|
|
void PutRawInteger(long value, uchar &out[], int &pos, int &cap) {
|
||
|
|
if(value == 0) { PutChar('0', out, pos, cap); return; }
|
||
|
|
if(value < 0) { PutChar('-', out, pos, cap); value = -value; }
|
||
|
|
uchar digits[20]; int n = 0;
|
||
|
|
while(value >= 100) {
|
||
|
|
int idx = (int)(value % 100) * 2; digits[n++] = g_digit_pairs[idx + 1]; digits[n++] = g_digit_pairs[idx]; value /= 100;
|
||
|
|
}
|
||
|
|
if(value >= 10) {
|
||
|
|
int idx = (int)value * 2; digits[n++] = g_digit_pairs[idx + 1]; digits[n++] = g_digit_pairs[idx];
|
||
|
|
} else digits[n++] = (uchar)('0' + (int)value);
|
||
|
|
CheckCap(n, pos, cap, out);
|
||
|
|
for(int j = n - 1; j >= 0; j--) out[pos++] = digits[j];
|
||
|
|
}
|
||
|
|
|
||
|
|
void PutRawDouble(int idx, uchar &out[], int &pos, int &cap) {
|
||
|
|
long data = tape[idx + 1]; int p = (int)(data >> 32); int l = (int)(data & 0xFFFFFFFF);
|
||
|
|
bool has_dot = false; bool has_exp = false;
|
||
|
|
for(int i = 0; i < l; i++) {
|
||
|
|
uchar c = buffer[p + i];
|
||
|
|
if(c == '.') has_dot = true;
|
||
|
|
if(c == 'e' || c == 'E') { has_exp = true; break; }
|
||
|
|
}
|
||
|
|
if(has_dot && !has_exp) {
|
||
|
|
while(l > 0 && buffer[p + l - 1] == '0') l--;
|
||
|
|
if(l > 0 && buffer[p + l - 1] == '.') l--;
|
||
|
|
}
|
||
|
|
if(l > 0) { CheckCap(l, pos, cap, out); ArrayCopy(out, buffer, pos, p, l); pos += l; }
|
||
|
|
else { CheckCap(1, pos, cap, out); out[pos++] = '0'; }
|
||
|
|
}
|
||
|
|
|
||
|
|
void CheckCap(int req, int pos, int &cap, uchar &out[]) {
|
||
|
|
if(pos + req >= cap) { cap = cap + req + 4096; ArrayResize(out, cap); }
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CJsonIterator: Fast Traversal |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
struct CJsonNode;
|
||
|
|
|
||
|
|
struct CJsonIterator {
|
||
|
|
CJsonContext *ctx;
|
||
|
|
int cur_idx;
|
||
|
|
int end_idx;
|
||
|
|
int steps_taken;
|
||
|
|
int total_count;
|
||
|
|
|
||
|
|
bool IsValid() { return (cur_idx < end_idx && steps_taken < total_count); }
|
||
|
|
void Next() {
|
||
|
|
if(!IsValid()) return;
|
||
|
|
if(ctx.GetType(cur_idx) == J_KEY) cur_idx += 2 + (int)(ctx.tape[cur_idx + 2] & 0xFFFFFFFF);
|
||
|
|
else cur_idx += (int)(ctx.tape[cur_idx] & 0xFFFFFFFF);
|
||
|
|
steps_taken++;
|
||
|
|
}
|
||
|
|
CJsonNode Val();
|
||
|
|
string Key();
|
||
|
|
};
|
||
|
|
|
||
|
|
struct CJsonFastDoubleIterator {
|
||
|
|
CJsonContext *ctx;
|
||
|
|
int cur_idx;
|
||
|
|
int end_idx;
|
||
|
|
|
||
|
|
bool IsValid() { return cur_idx < end_idx; }
|
||
|
|
void Next() { cur_idx += 2; }
|
||
|
|
double Val() {
|
||
|
|
union U { double d; long l; } u;
|
||
|
|
u.l = ctx.tape[cur_idx + 1];
|
||
|
|
return u.d;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CJsonNode: The Handle |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
struct CJsonNode {
|
||
|
|
CJsonContext *ctx;
|
||
|
|
int idx;
|
||
|
|
|
||
|
|
CJsonFastDoubleIterator begin_fast_double() {
|
||
|
|
CJsonFastDoubleIterator it; it.ctx = ctx;
|
||
|
|
if(!ctx || idx == -1 || ((ctx.tape[idx] >> 56) & 0xFF) != J_ARR) { it.cur_idx = 0; it.end_idx = 0; return it; }
|
||
|
|
it.cur_idx = idx + 1; it.end_idx = idx + (int)(ctx.tape[idx] & 0xFFFFFFFF);
|
||
|
|
return it;
|
||
|
|
}
|
||
|
|
|
||
|
|
CJsonNode operator[](string key) {
|
||
|
|
if(!ctx || idx == -1) return GetNull();
|
||
|
|
int klen = StringLen(key); if(klen == 0) return GetNull();
|
||
|
|
|
||
|
|
uchar kbuf[128];
|
||
|
|
int max_bytes = klen < 127 ? klen + 1 : 128;
|
||
|
|
int kbytes = StringToCharArray(key, kbuf, 0, max_bytes, CP_UTF8);
|
||
|
|
if(kbytes > 0 && kbuf[kbytes - 1] == 0) kbytes--;
|
||
|
|
|
||
|
|
uint h = 2166136261; int ki = 0;
|
||
|
|
for(; ki + 4 <= kbytes; ki += 4) {
|
||
|
|
h = (h ^ kbuf[ki]) * 16777619; h = (h ^ kbuf[ki + 1]) * 16777619;
|
||
|
|
h = (h ^ kbuf[ki + 2]) * 16777619; h = (h ^ kbuf[ki + 3]) * 16777619;
|
||
|
|
}
|
||
|
|
for(; ki < kbytes; ki++) h = (h ^ kbuf[ki]) * 16777619;
|
||
|
|
|
||
|
|
long head = ctx.tape[idx];
|
||
|
|
if(((head >> 56) & 0xFF) != J_OBJ) return GetNull();
|
||
|
|
|
||
|
|
int end = idx + (int)(head & 0xFFFFFFFF);
|
||
|
|
int cur = idx + 1;
|
||
|
|
uint match_packed = ((uint)(kbytes & 0xFF) << 24) | (h & 0xFFFFFF);
|
||
|
|
|
||
|
|
while(cur < end) {
|
||
|
|
if((uint)(ctx.tape[cur + 1] & 0xFFFFFFFF) == match_packed) {
|
||
|
|
CJsonNode node; node.ctx = ctx; node.idx = cur + 2; return node;
|
||
|
|
}
|
||
|
|
cur += 2 + (int)(ctx.tape[cur + 2] & 0xFFFFFFFF);
|
||
|
|
}
|
||
|
|
return GetNull();
|
||
|
|
}
|
||
|
|
|
||
|
|
CJsonNode operator[](int index) {
|
||
|
|
if(!ctx || idx == -1 || ctx.GetType(idx) != J_ARR) return GetNull();
|
||
|
|
int count = ctx.GetCount(idx); if(index < 0 || index >= count) return GetNull();
|
||
|
|
int cur = idx + 1;
|
||
|
|
for(int i = 0; i < index; i++) cur += (int)(ctx.tape[cur] & 0xFFFFFFFF);
|
||
|
|
CJsonNode node; node.ctx = ctx; node.idx = cur; return node;
|
||
|
|
}
|
||
|
|
|
||
|
|
CJsonIterator begin() {
|
||
|
|
CJsonIterator it; it.ctx = ctx;
|
||
|
|
if(!IsValid() || (ctx.GetType(idx) != J_OBJ && ctx.GetType(idx) != J_ARR)) { it.cur_idx = 0; it.end_idx = 0; return it; }
|
||
|
|
it.cur_idx = idx + 1; it.end_idx = idx + (int)(ctx.tape[idx] & 0xFFFFFFFF);
|
||
|
|
it.total_count = ctx.GetCount(idx); it.steps_taken = 0;
|
||
|
|
return it;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool IsValid() { return (ctx != NULL && idx != -1); }
|
||
|
|
bool IsNull() { return (!IsValid() || ctx.GetType(idx) == J_NULL); }
|
||
|
|
bool IsString() { return (IsValid() && ctx.GetType(idx) == J_STR); }
|
||
|
|
bool IsNumber() { return (IsValid() && (ctx.GetType(idx) == J_INT || ctx.GetType(idx) == J_DBL)); }
|
||
|
|
bool IsArray() { return (IsValid() && ctx.GetType(idx) == J_ARR); }
|
||
|
|
bool IsObject() { return (IsValid() && ctx.GetType(idx) == J_OBJ); }
|
||
|
|
|
||
|
|
string ToString() {
|
||
|
|
if(!IsValid()) return "";
|
||
|
|
switch(ctx.GetType(idx)) {
|
||
|
|
case J_STR: return ctx.GetStr(idx);
|
||
|
|
case J_BOOL: return ctx.GetBool(idx) ? "true" : "false";
|
||
|
|
case J_INT: return IntegerToString(ctx.GetInt(idx));
|
||
|
|
case J_DBL: return DoubleToString(ToDouble(), 8);
|
||
|
|
case J_NULL: return "null";
|
||
|
|
default: return "";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
long ToInt() {
|
||
|
|
if(!IsValid()) return 0;
|
||
|
|
int t = ctx.GetType(idx);
|
||
|
|
if(t == J_INT) return ctx.GetInt(idx);
|
||
|
|
if(t == J_DBL) return (long)ToDouble();
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Pure manual extraction to optimize out CharArrayToString allocations
|
||
|
|
double ToDouble() {
|
||
|
|
if(!IsValid()) return double("nan");
|
||
|
|
int t = ctx.GetType(idx);
|
||
|
|
if(t == J_DBL) {
|
||
|
|
long data = ctx.tape[idx + 1];
|
||
|
|
int p = (int)(data >> 32);
|
||
|
|
int l = (int)(data & 0xFFFFFFFF);
|
||
|
|
|
||
|
|
// Zero-heap high performance manual string slice evaluation
|
||
|
|
double val = 0.0; bool neg = false; int i = 0;
|
||
|
|
if(i < l && ctx.buffer[p] == '-') { neg = true; i++; }
|
||
|
|
while(i < l && ctx.buffer[p + i] >= '0' && ctx.buffer[p + i] <= '9') {
|
||
|
|
val = val * 10.0 + (ctx.buffer[p + i] - '0'); i++;
|
||
|
|
}
|
||
|
|
if(i < l && ctx.buffer[p + i] == '.') {
|
||
|
|
i++; double weight = 0.1;
|
||
|
|
while(i < l && ctx.buffer[p + i] >= '0' && ctx.buffer[p + i] <= '9') {
|
||
|
|
val += (ctx.buffer[p + i] - '0') * weight; weight *= 0.1; i++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if(i < l && (ctx.buffer[p + i] == 'e' || ctx.buffer[p + i] == 'E')) {
|
||
|
|
i++; bool exp_neg = false; int exp_val = 0;
|
||
|
|
if(i < l && ctx.buffer[p + i] == '-') { exp_neg = true; i++; }
|
||
|
|
else if(i < l && ctx.buffer[p + i] == '+') i++;
|
||
|
|
while(i < l && ctx.buffer[p + i] >= '0' && ctx.buffer[p + i] <= '9') {
|
||
|
|
exp_val = exp_val * 10 + (ctx.buffer[p + i] - '0'); i++;
|
||
|
|
}
|
||
|
|
if(exp_val > 0 && exp_val <= 308) {
|
||
|
|
if(exp_neg) val *= g_Exp10[exp_val]; else val *= g_Pow10[exp_val];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return neg ? -val : val;
|
||
|
|
}
|
||
|
|
if(t == J_INT) return (double)ctx.GetInt(idx);
|
||
|
|
return double("nan");
|
||
|
|
}
|
||
|
|
|
||
|
|
string ToString(string def) { return (IsValid() && ctx.GetType(idx) == J_STR) ? ctx.GetStr(idx) : def; }
|
||
|
|
long ToInt(long def) { return (IsValid() && IsNumber()) ? ToInt() : def; }
|
||
|
|
double ToDouble(double def) { return (IsValid() && IsNumber()) ? ToDouble() : def; }
|
||
|
|
bool ToBool(bool def) { return (IsValid() && ctx.GetType(idx) == J_BOOL) ? ctx.GetBool(idx) : def; }
|
||
|
|
|
||
|
|
int Size() {
|
||
|
|
if(!IsValid()) return 0;
|
||
|
|
int t = ctx.GetType(idx);
|
||
|
|
return (t == J_ARR || t == J_OBJ) ? ctx.GetCount(idx) : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool HasKey(string key) { return this[key].IsValid(); }
|
||
|
|
|
||
|
|
int GetKeys(string &dst[]) {
|
||
|
|
if(!IsObject()) { ArrayResize(dst, 0); return 0; }
|
||
|
|
int count = Size(); ArrayResize(dst, count);
|
||
|
|
CJsonIterator it = begin(); int i = 0;
|
||
|
|
while(it.IsValid() && i < count) { dst[i++] = it.Key(); it.Next(); }
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Equals(string val) {
|
||
|
|
if(!IsValid() || ctx.GetType(idx) != J_STR) return false;
|
||
|
|
long data = ctx.tape[idx + 1]; int p = (int)(data >> 32); int l = (int)(data & 0xFFFFFFFF);
|
||
|
|
if(StringLen(val) != l) return false;
|
||
|
|
for(int i = 0; i < l; i++) {
|
||
|
|
if(ctx.buffer[p + i] != (uchar)StringGetCharacter(val, i)) return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
string Serialize() {
|
||
|
|
if(!IsValid()) return "null";
|
||
|
|
uchar out[]; int pos = 0; int cap = 1024; ArrayResize(out, cap);
|
||
|
|
ctx.WriteNode(idx, out, pos, cap);
|
||
|
|
return CharArrayToString(out, 0, pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
CJsonNode GetNull() { CJsonNode n; n.ctx = NULL; n.idx = -1; return n; }
|
||
|
|
};
|
||
|
|
|
||
|
|
CJsonNode CJsonIterator::Val() {
|
||
|
|
if(!IsValid()) { CJsonNode n; n.ctx = NULL; n.idx = -1; return n; }
|
||
|
|
CJsonNode n; n.ctx = ctx; n.idx = (ctx.GetType(cur_idx) == J_KEY) ? cur_idx + 2 : cur_idx;
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
string CJsonIterator::Key() {
|
||
|
|
if(!IsValid() || ctx.GetType(cur_idx) != J_KEY) return "";
|
||
|
|
long kp = ctx.tape[cur_idx + 1];
|
||
|
|
return ctx.Unescape((int)(kp >> 32), (int)((kp >> 24) & 0xFF));
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CJsonBuilder: Optimized Structure Processing |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CJsonBuilder {
|
||
|
|
private:
|
||
|
|
uchar m_buf[];
|
||
|
|
int m_pos;
|
||
|
|
int m_cap;
|
||
|
|
bool m_stack_first[64];
|
||
|
|
int m_sp;
|
||
|
|
|
||
|
|
public:
|
||
|
|
CJsonBuilder(int initial_cap = 1024) {
|
||
|
|
m_cap = initial_cap; ArrayResize(m_buf, m_cap);
|
||
|
|
m_pos = 0; m_sp = 0; m_stack_first[0] = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void Clear() { m_pos = 0; m_sp = 0; m_stack_first[0] = true; }
|
||
|
|
string Build() { return CharArrayToString(m_buf, 0, m_pos, CP_UTF8); }
|
||
|
|
|
||
|
|
CJsonBuilder *Obj() { Comma(); Put('{'); if(m_sp < 63) { m_sp++; m_stack_first[m_sp] = true; } return &this; }
|
||
|
|
CJsonBuilder *EndObj() { if(m_sp > 0) m_sp--; Put('}'); m_stack_first[m_sp] = false; return &this; }
|
||
|
|
CJsonBuilder *Arr() { Comma(); Put('['); if(m_sp < 63) { m_sp++; m_stack_first[m_sp] = true; } return &this; }
|
||
|
|
CJsonBuilder *EndArr() { if(m_sp > 0) m_sp--; Put(']'); m_stack_first[m_sp] = false; return &this; }
|
||
|
|
|
||
|
|
CJsonBuilder *Key(string k) { Comma(); PutEncodedStr(k); Put(':'); m_stack_first[m_sp] = true; return &this; }
|
||
|
|
CJsonBuilder *Val(string v) { Comma(); PutEncodedStr(v); return &this; }
|
||
|
|
CJsonBuilder *Val(int v) { Comma(); PutRaw(IntegerToString(v)); return &this; }
|
||
|
|
CJsonBuilder *Val(long v) { Comma(); PutRaw(IntegerToString(v)); return &this; }
|
||
|
|
CJsonBuilder *Val(double v) { Comma(); PutRaw(DoubleToString(v)); return &this; }
|
||
|
|
CJsonBuilder *Val(bool v) { Comma(); PutRaw(v ? "true" : "false"); return &this; }
|
||
|
|
CJsonBuilder *ValidJson(string fragment) { Comma(); PutRaw(fragment); return &this; }
|
||
|
|
CJsonBuilder *Null() { Comma(); PutRaw("null"); return &this; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
void Comma() { if(!m_stack_first[m_sp]) Put(','); m_stack_first[m_sp] = false; }
|
||
|
|
void Put(uchar c) { if(m_pos >= m_cap) Expand(1); m_buf[m_pos++] = c; }
|
||
|
|
|
||
|
|
void PutRaw(string s) {
|
||
|
|
int l = StringLen(s); int max_bytes = l * 4 + 1;
|
||
|
|
if(m_pos + max_bytes >= m_cap) Expand(max_bytes);
|
||
|
|
int written = StringToCharArray(s, m_buf, m_pos, WHOLE_ARRAY, CP_UTF8);
|
||
|
|
if(written > 0 && m_buf[m_pos + written - 1] == 0) written--;
|
||
|
|
m_pos += written;
|
||
|
|
}
|
||
|
|
|
||
|
|
void PutEncodedStr(string s) {
|
||
|
|
Put('"');
|
||
|
|
int l = StringLen(s); if(l == 0) { Put('"'); return; }
|
||
|
|
|
||
|
|
// Convert source string once into a local processing array to skip StringGetCharacter overhead
|
||
|
|
uchar s_arr[];
|
||
|
|
int s_bytes = StringToCharArray(s, s_arr, 0, WHOLE_ARRAY, CP_UTF8);
|
||
|
|
if(s_bytes > 0 && s_arr[s_bytes - 1] == 0) s_bytes--;
|
||
|
|
|
||
|
|
if(m_pos + s_bytes + 128 >= m_cap) Expand(s_bytes + 128);
|
||
|
|
|
||
|
|
for(int i = 0; i < s_bytes; i++) {
|
||
|
|
uchar c = s_arr[i];
|
||
|
|
if(c >= 32 && c != '"' && c != '\\' && c < 127) {
|
||
|
|
m_buf[m_pos++] = c; continue;
|
||
|
|
}
|
||
|
|
if(c == '"') { Put('\\'); Put('"'); }
|
||
|
|
else if(c == '\\') { Put('\\'); Put('\\'); }
|
||
|
|
else if(c == 8) { Put('\\'); Put('b'); }
|
||
|
|
else if(c == 12) { Put('\\'); Put('f'); }
|
||
|
|
else if(c == 10) { Put('\\'); Put('n'); }
|
||
|
|
else if(c == 13) { Put('\\'); Put('r'); }
|
||
|
|
else if(c == 9) { Put('\\'); Put('t'); }
|
||
|
|
else if(c < 32) { Put('\\'); Put('u'); Put('0'); Put('0'); Put(HexChar((c >> 4) & 0xF)); Put(HexChar(c & 0xF)); }
|
||
|
|
else m_buf[m_pos++] = c; // Fall-through for safe extended UTF-8 characters
|
||
|
|
}
|
||
|
|
Put('"');
|
||
|
|
}
|
||
|
|
|
||
|
|
uchar HexChar(int v) { return (uchar)(v < 10 ? '0' + v : 'a' + (v - 10)); }
|
||
|
|
void Expand(int add) { m_cap += add + 4096; ArrayResize(m_buf, m_cap); }
|
||
|
|
};
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CJson: The Document Root Wrapper |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CJson {
|
||
|
|
CJsonContext ctx;
|
||
|
|
public:
|
||
|
|
bool Parse(string json) { return ctx.Parse(json); }
|
||
|
|
bool ParseBuffer(uchar &data[], int data_len) { return ctx.ParseBuffer(data, data_len); }
|
||
|
|
|
||
|
|
CJsonNode GetRoot() {
|
||
|
|
if(ctx.tape_pos == 0) { CJsonNode n; n.ctx = NULL; n.idx = -1; return n; }
|
||
|
|
CJsonNode n; n.ctx = &ctx; n.idx = 0; return n;
|
||
|
|
}
|
||
|
|
CJsonNode operator[](string key) { return GetRoot()[key]; }
|
||
|
|
CJsonNode operator[](int index) { return GetRoot()[index]; }
|
||
|
|
string Serialize(bool pretty = false) { return ctx.Serialize(pretty); }
|
||
|
|
int GetLastError() { return ctx.last_error; }
|
||
|
|
void GetErrorPos(int &line, int &col) { ctx.GetErrorLocation(ctx.err_pos, line, col); }
|
||
|
|
};
|