//+------------------------------------------------------------------+ //| fast_json.mqh | //| AI Toolkit - Neural Execution Unified System | //| | //| Module: Json (The "Ludicrous Speed" Edition V3.9) | //+------------------------------------------------------------------+ #property copyright "AIToolkit Framework" #property version "3.90" #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 & Fast Lookup Cache | //+------------------------------------------------------------------+ 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]; // Micro-optimization: Global lookaside buffer to eliminate heap allocations inside loops uchar g_shared_kbuf[2048]; uchar g_shared_sbuf[4096]; 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 | //+------------------------------------------------------------------+ class CJsonContext { public: long tape[]; int tape_pos; uchar buffer[]; int len; int last_error; string error_msg; int err_pos; int stack_node[256]; int stack_state[256]; int stack_count[256]; int sp; // Ultra-Fast structural properties cache int last_searched_obj; uint last_searched_hash; int last_searched_res; CJsonContext() { last_searched_obj = -1; last_searched_hash = 0; last_searched_res = -1; } inline int GetType(int idx) { return (int)((tape[idx] >> 56) & 0xFF); } inline long GetSize(int idx) { return tape[idx] & 0xFFFFFFFF; } inline int GetCount(int idx) { return (int)((tape[idx] >> 32) & 0xFFFFFF); } inline bool GetBool(int idx) { return (tape[idx + 1] & 1) == 1; } inline long GetInt(int idx) { return tape[idx + 1]; } inline 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)); } inline bool Reserve(int size) { int current = ArraySize(tape); if(current < size) { if(ArrayResize(tape, size + 8192) == -1) { last_error = JSON_ERR_STACK_OVERFLOW; return false; } } return true; } inline int ScanString(int ptr) { int start = ptr; int swar_limit = len - 8; while(ptr <= swar_limit) { 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; } inline 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; } inline bool ParseNumber(int &cur, uchar &c) { int start = cur; long int_val; c ^= '0'; if(this.GetInteger(int_val, cur, c)) { if(c == ('.' ^ '0') || c == ('e' ^ '0') || c == ('E' ^ '0')) { while(cur < len && ((buffer[cur] >= '0' && buffer[cur] <= '9') || buffer[cur] == '.' || buffer[cur] == '-' || buffer[cur] == '+' || buffer[cur] == 'e' || buffer[cur] == 'E')) cur++; 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 z_s = ((word ^ SWAR_SLASH) - SWAR_LO) & ~(word ^ SWAR_SLASH) & 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); } bool Parse(string json_str) { if(!g_init) InitTables(); tape_pos = 0; sp = 0; last_error = JSON_OK; last_searched_obj = -1; int str_len = StringLen(json_str); if(ArraySize(buffer) < str_len + 8) ArrayResize(buffer, str_len + 8, 8192); 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; last_searched_obj = -1; if(ArraySize(buffer) < data_len + 8) ArrayResize(buffer, data_len + 8, 8192); 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 + 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) { while(cur < len && buffer[cur] <= ' ') 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 == '{') { int idx = tape_pos++; stack_state[sp - 1] = ST_OBJ; stack_node[sp - 1] = idx; stack_count[sp - 1] = 0; cur++; } else if(c == '[') { 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; while(cur < len && buffer[cur] <= ' ') 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 + 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: if(pos + 4 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = 'n'; out[pos++] = 'u'; out[pos++] = 'l'; out[pos++] = 'l'; break; case J_BOOL: if(GetBool(idx)) { if(pos + 4 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = 't'; out[pos++] = 'r'; out[pos++] = 'u'; out[pos++] = 'e'; } else { if(pos + 5 >= cap) { cap += 4096; ArrayResize(out, cap); } 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); if(pos + l + 2 >= cap) { cap += l + 4096; ArrayResize(out, cap); } out[pos++] = '"'; if(l > 0) { ArrayCopy(out, buffer, pos, p, l); pos += l; } out[pos++] = '"'; break; } case J_ARR: { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '['; int count = GetCount(idx); int cur = idx + 1; for(int i = 0; i < count; i++) { if(i > 0) { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = ','; } WriteNode(cur, out, pos, cap); cur += MACRO_GET_STEP(cur); } if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = ']'; break; } case J_OBJ: { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '{'; int count = GetCount(idx); int cur = idx + 1; int emitted = 0; while(emitted < count) { if(GetType(cur) == J_KEY) { if(emitted > 0) { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = ','; } long kp = tape[cur + 1]; int kptr = (int)(kp >> 32); int klen = (int)((kp >> 24) & 0xFF); if(pos + klen + 3 >= cap) { cap += klen + 4096; ArrayResize(out, cap); } 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++; } if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '}'; break; } } } void WriteNodePretty(int idx, uchar &out[], int &pos, int &cap, int depth) { if(idx >= tape_pos) return; switch (GetType(idx)) { case J_NULL: WriteNode(idx, out, pos, cap); break; case J_BOOL: WriteNode(idx, out, pos, cap); break; case J_INT: PutRawInteger(GetInt(idx), out, pos, cap); break; case J_DBL: PutRawDouble(idx, out, pos, cap); break; case J_STR: WriteNode(idx, out, pos, cap); break; case J_ARR: { int count = GetCount(idx); if(count == 0) { if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '['; out[pos++] = ']'; return; } if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '['; out[pos++] = '\n'; int cur = idx + 1; for(int i = 0; i < count; i++) { if(i > 0) { if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = ','; out[pos++] = '\n'; } Indent(depth + 1, out, pos, cap); WriteNodePretty(cur, out, pos, cap, depth + 1); cur += MACRO_GET_STEP(cur); } if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '\n'; Indent(depth, out, pos, cap); out[pos++] = ']'; break; } case J_OBJ: { int count = GetCount(idx); if(count == 0) { if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '{'; out[pos++] = '}'; return; } if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '{'; out[pos++] = '\n'; int cur = idx + 1; int emitted = 0; while(emitted < count) { if(GetType(cur) == J_KEY) { if(emitted > 0) { if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = ','; out[pos++] = '\n'; } Indent(depth + 1, out, pos, cap); long kp = tape[cur + 1]; int kptr = (int)(kp >> 32); int klen = (int)((kp >> 24) & 0xFF); if(pos + klen + 4 >= cap) { cap += klen + 4096; ArrayResize(out, cap); } 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++; } if(pos + 2 >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '\n'; Indent(depth, out, pos, cap); out[pos++] = '}'; break; } } } inline void Indent(int depth, uchar &out[], int &pos, int &cap) { int req = depth * 2; if(pos + req >= cap) { cap += req + 4096; ArrayResize(out, cap); } for(int i = 0; i < depth; i++) { out[pos++] = ' '; out[pos++] = ' '; } } void PutRawInteger(long value, uchar &out[], int &pos, int &cap) { if(value == 0) { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '0'; return; } if(value < 0) { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '-'; 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); if(pos + n >= cap) { cap += n + 4096; ArrayResize(out, cap); } 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) { if(pos + l >= cap) { cap += l + 4096; ArrayResize(out, cap); } ArrayCopy(out, buffer, pos, p, l); pos += l; } else { if(pos >= cap) { cap += 4096; ArrayResize(out, cap); } out[pos++] = '0'; } } }; //+------------------------------------------------------------------+ //| CJsonIterator | //+------------------------------------------------------------------+ struct CJsonNode; struct CJsonIterator { CJsonContext *ctx; int cur_idx; int end_idx; int steps_taken; int total_count; inline bool IsValid() { return (cur_idx < end_idx && steps_taken < total_count); } inline 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; inline bool IsValid() { return cur_idx < end_idx; } inline void Next() { cur_idx += 2; } inline double Val() { union U { double d; long l; } u; u.l = ctx.tape[cur_idx + 1]; return u.d; } }; //+------------------------------------------------------------------+ //| CJsonNode | //+------------------------------------------------------------------+ struct CJsonNode { CJsonContext *ctx; int idx; CJsonFastDoubleIterator begin_fast_double() { CJsonFastDoubleIterator it; it.ctx = ctx; if(!ctx || idx == -1 || 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); return it; } CJsonNode operator[](string key) { if(!ctx || idx == -1) return GetNull(); int klen = StringLen(key); if(klen == 0) return GetNull(); // Use shared memory lookup cache to bypass heap dynamic allocation int max_bytes = klen < 2047 ? klen + 1 : 2048; int kbytes = StringToCharArray(key, g_shared_kbuf, 0, max_bytes, CP_UTF8); if(kbytes > 0 && g_shared_kbuf[kbytes - 1] == 0) kbytes--; uint h = 2166136261; int ki = 0; for(; ki + 4 <= kbytes; ki += 4) { h = (h ^ g_shared_kbuf[ki]) * 16777619; h = (h ^ g_shared_kbuf[ki + 1]) * 16777619; h = (h ^ g_shared_kbuf[ki + 2]) * 16777619; h = (h ^ g_shared_kbuf[ki + 3]) * 16777619; } for(; ki < kbytes; ki++) h = (h ^ g_shared_kbuf[ki]) * 16777619; // Check Lookaside Search Cache Result if(ctx.last_searched_obj == idx && ctx.last_searched_hash == h) { if(ctx.last_searched_res != -1) { CJsonNode node; node.ctx = ctx; node.idx = ctx.last_searched_res; return node; } return GetNull(); } 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) { ctx.last_searched_obj = idx; ctx.last_searched_hash = h; ctx.last_searched_res = cur + 2; CJsonNode node; node.ctx = ctx; node.idx = cur + 2; return node; } cur += 2 + (int)(ctx.tape[cur + 2] & 0xFFFFFFFF); } ctx.last_searched_obj = idx; ctx.last_searched_hash = h; ctx.last_searched_res = -1; 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; } inline bool IsValid() { return (ctx != NULL && idx != -1); } inline bool IsNull() { return (!IsValid() || ctx.GetType(idx) == J_NULL); } inline bool IsString() { return (IsValid() && ctx.GetType(idx) == J_STR); } inline bool IsNumber() { return (IsValid() && (ctx.GetType(idx) == J_INT || ctx.GetType(idx) == J_DBL)); } inline bool IsArray() { return (IsValid() && ctx.GetType(idx) == J_ARR); } inline 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); default: return ""; } } inline 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; } // Hybrid fast-extract double parsing strategy inline 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); if(l < 32) { // Fast-path route for compact numbers 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; } return StringToDouble(CharArrayToString(ctx.buffer, p, l, CP_UTF8)); } 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 = 2048; 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 | //+------------------------------------------------------------------+ 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 = 4096) { 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, 8)); 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: inline void Comma() { if(!m_stack_first[m_sp]) Put(','); m_stack_first[m_sp] = false; } inline void Put(uchar c) { if(m_pos >= m_cap) { m_cap += 8192; ArrayResize(m_buf, m_cap); } 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) { m_cap += max_bytes + 8192; ArrayResize(m_buf, m_cap); } 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; } int max_bytes = l < 4095 ? l + 1 : 4096; int s_bytes = StringToCharArray(s, g_shared_sbuf, 0, max_bytes, CP_UTF8); if(s_bytes > 0 && g_shared_sbuf[s_bytes - 1] == 0) s_bytes--; if(m_pos + s_bytes + 128 >= m_cap) { m_cap += s_bytes + 8192; ArrayResize(m_buf, m_cap); } for(int i = 0; i < s_bytes; i++) { uchar c = g_shared_sbuf[i]; if(c >= 32 && c != '"' && c != '\\' && c < 127) { m_buf[m_pos++] = c; continue; } if(c == '"') { m_buf[m_pos++] = '\\'; m_buf[m_pos++] = '"'; } else if(c == '\\') { m_buf[m_pos++] = '\\'; m_buf[m_pos++] = '\\'; } else if(c == 10) { m_buf[m_pos++] = '\\'; m_buf[m_pos++] = 'n'; } else if(c == 13) { m_buf[m_pos++] = '\\'; m_buf[m_pos++] = 'r'; } else if(c == 9) { m_buf[m_pos++] = '\\'; m_buf[m_pos++] = 't'; } else m_buf[m_pos++] = c; } Put('"'); } }; 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; } };